Search code examples
reactjssyncfusion

Forcing a component to update on change


I am trying to get this componentFunction to re-render with the new data field on the state change that occurs changeValue and I don't know where I'm going wrong.

class Classname extends React.Component {
  constructor() {
    super();
    this.state = {
      value: "OriginalString",
    };
  }

  changeValue = (newString) => {
    this.setState({ value: newString });
    this.forceUpdate();
  };

  componentFunction = () => {
    return (
      <div>
        <component data={this.state.value} />
      </div>
    );
  };

  render() {
    return (
      <div>
        <button
          onClick={() => {
            this.changeValue("updatedString");
          }}
        >
          Update
        </button>

        <div className="control-section">
          <DashboardLayoutComponent
            id="dashboard_default"
            columns={5}
            cellSpacing={this.cellSpacing}
            allowResizing={false}
            resizeStop={this.onPanelResize.bind(this)}
          >
            <PanelsDirective>
              <PanelDirective
                sizeX={5}
                sizeY={2}
                row={0}
                col={0}
                content={this.componentFunction}
              />
            </PanelsDirective>
          </DashboardLayoutComponent>
        </div>
      </div>
    );
  }
}

ReactDOM.render(<Classname />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>


Solution

  • Issue

    The issue here is a stale enclosure of this.state.value in componentFunction.

    Solution(s)

    From what I can tell, the content prop of PanelDirective expects anything that returns, or resolves, to valid JSX (JSX attribute). A function callback to provide the content, a React component, or JSX literal all work.

    1. Callback to reenclose updated state. Convert to a curried function that can enclose the current state when component is rendered. When attaching the callback you invoke the first function and pass the state value, the returned function is what PanelDirective will use when calling for the content value.

      componentFunction = (data) => () => (
        <div>
          <component data={data} />
        </div>
      );
      

      ...

      <PanelDirective
        sizeX={5}
        sizeY={2}
        row={0}
        col={0}
        content={this.componentFunction(this.state.value)}
      />
      
    2. React component. Convert componentFucntion to a React component and pass.

      ComponentFunction = ({ data }) => (
        <div>
          <component data={data} />
        </div>
      );
      

      ...

      <PanelDirective
        sizeX={5}
        sizeY={2}
        row={0}
        col={0}
        content={<ComponentFunction data={this.state.value} />}
      />
      
    3. JSX literal

      <PanelDirective
        sizeX={5}
        sizeY={2}
        row={0}
        col={0}
        content={
          <div>
            <component data={this.state.value} />
          </div>
        }
      />
      

    Also, in case it wasn't obvious, you should remove the this.forceUpdate(); call in the changeValue handler. React state updates are sufficient in triggering the component to rerender.