140

I wanted to check what happens when you use this.setState multiple times (2 times for the sake of the discussion). I thought that the component will be rendered twice but apparently it's rendered only once. Another expectation I had was that maybe the second call for setState will run over the first one, but you guessed it - worked fine.

Link to a JSfiddle

var Hello = React.createClass({
  render: function() {
    return (
      <div>
        <div>Hello {this.props.name}</div>
        <CheckBox />
      </div>
    );
  }
});

var CheckBox = React.createClass({
  getInitialState: function() {
    return {
      alex: 0
    };
  },

  handleChange: function(event) {
    this.setState({
      value: event.target.value
    });
    this.setState({
      alex: 5
    });
  },

  render: function() {
    alert('render');
    return (
      <div>
        <label htmlFor="alex">Alex</label>
        <input type="checkbox" onChange={this.handleChange} name="alex" />
        <div>{this.state.alex}</div>
      </div>
    );
  }
});

ReactDOM.render(
  <Hello name="World" />,
  document.getElementById('container')
);

As you'll see, an alert that says 'render' pops up on every render.

Do you have an explanation for why it worked properly?

3
  • 2
    React is pretty clever and will only re-render when the state required to render is changed. In your render method you're only using this.state.alex - what happens if you add an element that depends on this.state.value as well? Commented Nov 9, 2015 at 16:34
  • 1
    @MartinWedvich It will break in case I'm dependent on it. For that you have the 'getInitialState' method - to set the default values so your app won't break. Commented Nov 10, 2015 at 8:21
  • 1
    related, and useful: stackoverflow.com/questions/48563650/… Commented Jun 26, 2018 at 15:11

2 Answers 2

160

React batches state updates that occur in event handlers and lifecycle methods. Thus, if you update state multiple times in a <div onClick /> handler, React will wait for event handling to finish before re-rendering.

To be clear, this only works in React-controlled synthetic event handlers and lifecycle methods. State updates are not batched in AJAX and setTimeout event handlers, for example.

UPDATE

With the release of React v18.0, state updates within non-React events (promises, setTimeout etc.) are also batched by default.

Ref - https://reactjs.org/blog/2022/03/29/react-v18.html#new-feature-automatic-batching

Sign up to request clarification or add additional context in comments.

9 Comments

Thanks, it makes sense, any idea how is it done behind the scenes?
Since React fully controls synthetic event handlers (like <div onClick />) and component lifecycle methods, it knows that it can safely change the behavior of setState for the duration of the call to batch state updates and wait to flush. In an AJAX or setTimeout handler by contrast, React has no way to know when our handler has finished executing. Technically, React queues state updates in both cases, but flushes immediately outside of a React-controlled handler.
@neurosnap I don't think the docs go into much detail on this. It is mentioned abstractly in the State and Lifecycle section and the setState docs. See the code for ReactDefaultBatchingStrategy, which is currently the only official batching strategy.
@BenjaminToueg I believe you can use ReactDOM.unstable_batchedUpdates(function) to batch setState outside of React event handlers. As the name implies, you'll void your warranty.
|
61

The setState() method does not immediately update the state of the component, it just puts the update in a queue to be processed later. React may batch multiple update requests together to make rendering more efficient. Due to this, special precautions must be made when you try to update the state based on the component's previous state.

For example, the following code will only increment the state value attribute by 1 even though it was called 4 times:

 class Counter extends React.Component{
   constructor(props){
     super(props)
    //initial state set up
     this.state = {value:0}
   }
   componentDidMount(){
    //updating state
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
   }
   render(){
    return <div>Message:{this.state.value}</div>
   }
}

In order to use a state after it has been updated, do all logic in the callback argument:

//this.state.count is originally 0
this.setState({count:42}, () => {
  console.log(this.state.count)
//outputs 42
})

The setState(updater,[callback]) method can take in an updater function as its first argument to update the state based on the previous state and properties. The return value of the updater function will be shallowly merged with the previous component state. The method updates the state asynchronously, so a there is an option callback that will be called once the state has finished updating completely.

Example:

this.setState((prevState, props) => { 
return {attribute:"value"}
})

Here is an example of how to update the state based on previous state:

    class Counter extends React.Component{
      constructor(props) {
        super(props)
    //initial state set up
        this.state = {message:"initial message"}
    }
      componentDidMount() {
    //updating state
        this.setState((prevState, props) => {
          return {message: prevState.message + '!'}
        })
     }
     render(){
       return <div>Message:{this.state.message}</div>
     }
  }

5 Comments

Ah, I did not know about the setState(updater,[callback]), it's like an a less verbose Object.assign thanks for that!
@Biboswan, hi , I am new to React and wanted to ask you that is it true that all four setStates inside CompountDidMount method are merged and ONLY THE LAST setState will be executed but the other 3 will be ignored?
For me, the key was to declare previousState parameter and use that in my updater function.
Any idea how React decides whether to batch the update or to apply it immediately? I am talking about how it does this internally at code level.
@darKnight it is mostly based on the concept of event loop and 16ms goes into 1 frame thing (60 frames per second). For best possible discussion I would suggest you to ask in the internals channel of reactiflux server in discord.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.