How to handle asynchronous calls to setState in React?

All we need is an easy explanation of the problem, so here it is.

I have a method that toggles a boolean value in state by copying the value then updating state:

toggleSelected = () => {
    let selected = this.state.lists.selected;
    selected = !selected;
    this.setState({
      // update state
    });
};

I have another method, fired by an onClick handler, that calls toggleSelected twice:

switchList = (listName) => {
    const currList = this.getCurrentList();
    if(listName === currList.name) return;
    this.toggleSelected(listName);
    this.toggleSelected(currList);
};

However it appears that the state doesn’t finish getting set from the first call by the time the second call runs; if I set a timeout on the second call, it works fine.

What’s the correct way to do this?

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

An alternative to what @SLaks suggested, useful sometimes, is using the setState(new_state, callback) method. The callback will be run once the state is updated and “visible”.

In general, setState will not change the state immediately so that it is visible in this.state. From the docs

setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall.

Method 2

As noted in the React State/Lifecycle docs, the correct way to update state based on previous state is by passing a function into the setState() call, eg:

toggleSelected() {
  this.setState((prevState, props) => {
    return {
      lists: {
        selected : ! prevState.lists.selected
      }
    };
  });
}

Method 3

This is why you should always pass a lambda to setState(), so that it will give you the actual current state:

this.setState(state => ({ ..., selected: !state.lists.selected }))

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply