Why Is setState()
Asynchronous In React?
Or Is It Synchronous?
The Problem
In React, we use setState()
to update the state of any component. Now setState()
does not immediately mutate this state, rather it creates a pending state transition. Accessing the state immediately after calling setState()
returns the existing value and not the updated one. As beginners in React, I’m sure most of us would have often faced this problem.
There is no guarantee of synchronous operation on setState()
and calls may be batched for performance gains. It is easy to forget that the setState()
is asynchronous, making it tricky for us to debug issues in your code. The setState()
also does not return a Promise. Using async
/await
or anything similar will not work. There is also another case, where we tend to use multiple setState()
functions within the same block, and there are times where the state isn’t properly updated.
Possible solution
Oftentimes we call setState()
with one parameter, but actually, the method supports two arguments. The second argument that you can pass is a callback function, that will always be executed after the state has been updated.
Let’s have a look at an example of the problem:
...
state = {value: 5}
...
...// --WRONG WAY-- //this.setState({value: this.state.value + 1})
console.log(this.state.value) // Prints 5 and not 6
The solution to write a better setState
function is:
// --RIGHT WAY-- //this.setState((prevState) => ({
value: prevState.value +1}), () => {
console.log(this.state.value)});
// Provide a callback to setState as second argument
The right way to update the state and access the updated value is given above. We pass a callback function as a second argument to the setState()
function which gets invoked after the state has been updated. It should also be noted that using a function as a first parameter to return an object is a very good way of writing React code, as you can get access to the current value of the state (prevState
). It is also a good practice to use one setState()
function in a block to avoid problems when the state is updated.
Why does this happen?
If you have a look at the code inside the setState()
function in React’s codebase, you will find that setState()
is not at all an asynchronous function and it is always synchronous. It’s just that it calls enqueueState or enqueueCallback when updated behind the scenes, and thus its execution feels like it is async.
So, what’s actually sync or async are the effects of calling setState()
in React — the reconciliation algorithm does the virtual DOM comparisons and calls render to update the real DOM.
React batches updates and flushes it out once per frame for performance reasons. However, in some cases React has no control over the batching, hence updates are made synchronously available, for example in AJAX, setTimeOut()
, etc.
Conclusion
Usually, updating state happens on the next render, but it can sometimes be batched for performance. This batching may not be controlled by React, and therefore setState()
may not be always sequential. So, make use of the second argument if you wanted to make sequential updates to the state.
I hope you learned something new from this! Thanks for reading!