0
votes

In the documentation for setState it has this to say:

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.

So I understand if you have an eventhandler like:

handleClick() {
  this.stuff1();
  this.stuff2();
}

stuff1() {
  this.setState({...});
}

stuff2() {
  doSomethingWithState(this.state);
  this.setState({...});
}

then in the stuff2 method you might not (or is this guaranteed you won't..) see the updates to state from stuff1. React provides a way of 'safely' accessing the previous state by supplying a function to setState() instead of an object. But presumably you could work around this by keeping track of the state yourself as you handle the event and call setState() once at the end of your method. So maybe there is another reason option for supplying a function to setState(). What if other changes to the state could have been batched before the handleClick method is called?

For example if handleClick() is called twice then am I guaranteed to see the state changes from the first call to handleClick() in the second call to handleClick()? or are there other ways the state could be dirty before my event handler has been called?

3

3 Answers

2
votes

Anytime you have a state that is derived from the previous state you can run into an issue of them being out of sync if you are not using the callback function vs passing in an object. In your last example you would only be guaranteed if you used the function instead. If you are relying on this.state you could easily run into a situation where handleClick is called once, the state transition gets queued up to be resolved later, you the have handleClick called again and the first state change is still pending and in queue so when you call this.state it will have the same state available to it as the first handleClick had which wouldn't be what you want.

this would be an example of using the callback function, assuming that doSomethingWithState returns the updated state object and is non mutative of course.

stuff2() {
  this.setState((state) => {
    const updatedState = doSomethingWithState(state);
    return state;
  })
}

this is a great article on using the function vs setState and also includes a codepen example demonstrating the problem. https://medium.com/@shopsifter/using-a-function-in-setstate-instead-of-an-object-1f5cfd6e55d1#.gm2t01g70

1
votes

If i understood you right, then the answer is no, your not guaranteed, as setState is an asynchronous operation - so it's kinda equivalent to the first 'problem' you mentioned. You can provide a callback function to the setState method that will be fired as the setState method finishes. Then, in the callback scope, you are guaranteed that the current state is updated and also provided with the old state for comparisons and other stuff....

0
votes

You can provide callback as said. Or since you want first function to get executed in handleClick(), what you can do is you can call the second function after setting the state in the first function. i.e.,

handleClick() {
  this.stuff1();
}

stuff1() {
  this.setState({...}, 
    () => { 
      this.stuff2(); 
    });
}

stuff2() {
  doSomethingWithState(this.state);
  this.setState({...});
}

I think this will provide the solution for the problem which is raised. Since we can't guarantee synchronous operation of calls to setState as it is an asynchronous operation.