Search code examples
javascriptreactjssetstate

Are setState's updater arguments always identical to this.state and this.props?


setState has a form that accepts a callback updater. This updater is invoked when this state transition actually occurs (since setState may be asynchronous), accepting state and props of the component as explicit arguments at the time of state setting. Is there any difference to referring to these arguments vs. this.state and this.props? The docs say:

That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument.

My motivation to know is that I have some pure functions of props and state in this component to calculate some derived values that are sometimes fed back into state. I'd like to avoid rewriting these functions when they're needed in setState.


Solution

  • One big difference is that this.state will only update after all currently queued setState calls have finished:

    class Component extends React.Component {
      constructor() {
        super();
        this.state = { count: 0 };
      }
      increment() {
        this.setState({ count: this.state.count + 1 });
      };
      render() {
        return (
          <div>
            <div>{this.state.count}</div>
            <button onClick={() => { this.increment(); this.increment(); }}>
              +
            </button>
          </div>
        );
      }
    }
    
    ReactDOM.render(
      <Component />,
      document.getElementById("react")
    );
    <div id="react"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>

    If the this.state updated immediately, you'd see the count jump from 0 to 2 to 4, etc, but it doesn't; it updates one-by-one, because this.state doesn't update immediately.

    In contrast, using the callback form's argument does provide you with the possibly-just-updated value.

    class Component extends React.Component {
      constructor() {
        super();
        this.state = { count: 0 };
      }
      increment() {
        this.setState(({ count }) => ({ count: count + 1 }));
      };
      render() {
        return (
          <div>
            <div>{this.state.count}</div>
            <button onClick={() => { this.increment(); this.increment(); }}>
              +
            </button>
          </div>
        );
      }
    }
    
    ReactDOM.render(
      <Component />,
      document.getElementById("react")
    );
    <div id="react"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>