149
votes

I have just found that in react this.setState() function in any component is asynchronous or is called after the completion of the function that it was called in.

Now I searched and found this blog (setState() State Mutation Operation May Be Synchronous In ReactJS)

Here he found that setState is async(called when stack is empty) or sync(called as soon as called) depending on how the change of state was triggered.

Now these two things are hard to digest

  1. In the blog the setState function is called inside a function updateState, but what triggered the updateState function is not something that a called function would know about.
  2. Why would they make setState async as JS is single threaded language and this setState is not a WebAPI or server call so has to be done on JS's thread only. Are they doing this so that Re-Rendering does not stop all the event listeners and stuff, or there is some other design issue.
8
I wrote an article today that helps describe a bit of the climate around setState: medium.com/@agm1984/…agm1984
No one has answered exactly why setState is asynchronous clearlyAbhi

8 Answers

173
votes

You can call a function after the state value has updated:

this.setState({foo: 'bar'}, () => { 
    // Do something here. 
});

Also, if you have lots of states to update at once, group them all within the same setState:

Instead of:

this.setState({foo: "one"}, () => {
    this.setState({bar: "two"});
});

Just do this:

this.setState({
    foo: "one",
    bar: "two"
});
116
votes

1) setState actions are asynchronous and are batched for performance gains. This is explained in the documentation of setState.

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value. There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.


2) Why would they make setState async as JS is a single threaded language and this setState is not a WebAPI or server call?

This is because setState alters the state and causes rerendering. This can be an expensive operation and making it synchronous might leave the browser unresponsive.

Thus the setState calls are asynchronous as well as batched for better UI experience and performance.

17
votes

I know this question is old, but it has been causing a lot of confusion for many reactjs users for a long time, including me. Recently Dan Abramov (from the react team) just wrote up a great explanation as to why the nature of setState is async:

https://github.com/facebook/react/issues/11527#issuecomment-360199710

setState is meant to be asynchronous, and there are a few really good reasons for that in the linked explanation by Dan Abramov. This doesn't mean it will always be asynchronous - it mainly means that you just can't depend on it being synchronous. ReactJS takes into consideration many variables in the scenario that you're changing the state in, to decide when the state should actually be updated and your component rerendered.
A simple example to demonstrate this, is that if you call setState as a reaction to a user action, then the state will probably be updated immediately (although, again, you can't count on it), so the user won't feel any delay, but if you call setState in reaction to an ajax call response or some other event that isn't triggered by the user, then the state might be updated with a slight delay, since the user won't really feel this delay, and it will improve performance by waiting to batch multiple state updates together and rerender the DOM fewer times.

8
votes

Good article here https://github.com/vasanthk/react-bits/blob/master/patterns/27.passing-function-to-setState.md

// assuming this.state.count === 0
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
// this.state.count === 1, not 3

Solution
this.setState((prevState, props) => ({
  count: prevState.count + props.increment
}));

or pass callback this.setState ({.....},callback)

https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82 https://medium.freecodecamp.org/functional-setstate-is-the-future-of-react-374f30401b6b

7
votes

You can use the following wrap to make sync call

this.setState((state =>{
  return{
    something
  }
})
2
votes

Imagine incrementing a counter in some component:

  class SomeComponent extends Component{

    state = {
      updatedByDiv: '',
      updatedByBtn: '',
      counter: 0
    }

    divCountHandler = () => {
      this.setState({
        updatedByDiv: 'Div',
        counter: this.state.counter + 1
      });
      console.log('divCountHandler executed');
    }

    btnCountHandler = () => {
      this.setState({
        updatedByBtn: 'Button',
        counter: this.state.counter + 1
      });
      console.log('btnCountHandler executed');
    }
    ...
    ...
    render(){
      return (
        ...
        // a parent div
        <div onClick={this.divCountHandler}>
          // a child button
          <button onClick={this.btnCountHandler}>Increment Count</button>
        </div>
        ...
      )
    }
  }

There is a count handler attached to both the parent and the child components. This is done purposely so we can execute the setState() twice within the same click event bubbling context, but from within 2 different handlers.

As we would imagine, a single click event on the button would now trigger both these handlers since the event bubbles from target to the outermost container during the bubbling phase.

Therefore the btnCountHandler() executes first, expected to increment the count to 1 and then the divCountHandler() executes, expected to increment the count to 2.

However the count only increments to 1 as you can inspect in React Developer tools.

This proves that react

  • queues all the setState calls

  • comes back to this queue after executing the last method in the context(the divCountHandler in this case)

  • merges all the object mutations happening within multiple setState calls in the same context(all method calls within a single event phase is same context for e.g.) into one single object mutation syntax (merging makes sense because this is why we can update the state properties independently in setState() in the first place)

  • and passes it into one single setState() to prevent re-rendering due to multiple setState() calls (this is a very primitive description of batching).

Resultant code run by react:

this.setState({
  updatedByDiv: 'Div',
  updatedByBtn: 'Button',
  counter: this.state.counter + 1
})

To stop this behaviour, instead of passing objects as arguments to the setState method, callbacks are passed.

    divCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByDiv: 'Div',
              counter: prevState.counter + 1
            };
          });
          console.log('divCountHandler executed');
        }

    btnCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByBtn: 'Button',
              counter: prevState.counter + 1
            };
          });
      console.log('btnCountHandler executed');
    }

After the last method finishes execution and when react returns to process the setState queue, it simply calls the callback for each setState queued, passing in the previous component state.

This way react ensures that the last callback in the queue gets to update the state that all of its previous counterparts have laid hands on.

1
votes

Yes, setState() is asynchronous.

From the link: https://reactjs.org/docs/react-component.html#setstate

  • React does not guarantee that the state changes are applied immediately.
  • setState() does not always immediately update the component.
  • Think of setState() as a request rather than an immediate command to update the component.

Because they think
From the link: https://github.com/facebook/react/issues/11527#issuecomment-360199710

... we agree that setState() re-rendering synchronously would be inefficient in many cases

Asynchronous setState() makes life very difficult for those getting started and even experienced unfortunately:
- unexpected rendering issues: delayed rendering or no rendering (based on program logic)
- passing parameters is a big deal
among other issues.

Below example helped:

// call doMyTask1 - here we set state
// then after state is updated...
//     call to doMyTask2 to proceed further in program

constructor(props) {
    // ..

    // This binding is necessary to make `this` work in the callback
    this.doMyTask1 = this.doMyTask1.bind(this);
    this.doMyTask2 = this.doMyTask2.bind(this);
}

function doMyTask1(myparam1) {
    // ..

    this.setState(
        {
            mystate1: 'myvalue1',
            mystate2: 'myvalue2'
            // ...
        },    
        () => {
            this.doMyTask2(myparam1); 
        }
    );
}

function doMyTask2(myparam2) {
    // ..
}

Hope that helps.

0
votes

setState is asynchronous. You can see in this documentation by Reactjs

React intentionally “waits” until all components call setState() in their event handlers before starting to re-render. This boosts performance by avoiding unnecessary re-renders.

However, you might still be wondering why React doesn’t just update this.state immediately without re-rendering.

The reason is this would break the consistency between props and state, causing issues that are very hard to debug.

You can still perform functions if it is dependent on the change of the state value:

Option 1: Using callback function with setState

this.setState({
   value: newValue
},()=>{
   // It is an callback function.
   // Here you can access the update value
   console.log(this.state.value)
})

Option 2: using componentDidUpdate This function will be called whenever the state of that particular class changes.

componentDidUpdate(prevProps, prevState){
    //Here you can check if value of your desired variable is same or not.
    if(this.state.value !== prevState.value){
        // this part will execute if your desired variable updates
    }
}