Imagine the increment of the 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 ( ...
There is a counter handler attached to both the parent and child components. This is done intentionally, so we can execute setState () twice in the same click event, but from two different handlers.
As we might suggest, a single click on a button now launches both of these handlers, since the event bubbles from the target to the outermost container during the bubbling phase.
Therefore, btnCountHandler () is started first, expecting it to increment the counter to 1, and then execute the divCountHandler () command to increase the count to 2.
However, the counter only increases to 1, as you can check in React Developer tools.
It proves that react
queues for all setState calls
returns to this queue after the last method is executed in the context (in this case divCountHandler)
combines all mutations of objects that occur in several calls to setState, in the same context (all method calls within the same phase of the event are the same context, for example), into one syntax of the mutation of the object (merging makes sense, because that's why we can update state properties independently in setState () first)
and passes it to one setState () to prevent re-rendering due to the many calls to setState () (this is a very primitive description of batch processing).
The resulting code works from a reaction:
this.setState({ updatedByDiv: 'Div', updatedByBtn: 'Button', counter: this.state.counter + 1 })
To stop this behavior, 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 completes execution and when the reaction returns to process the setState queue, it simply calls a callback for each set set, passing it to the previous state of the component.
This method responds to the fact that the last callback in the queue receives an update of the state that all previous colleagues put into their hands.