- , 3 min read
To a whole lot of you this may come off as a really stupid question, but this was the first question that came into my mind when I first got to know about the asynchronous nature of setState hook. Well the answer is that setState doesn't return a promise that's why it cant be awaited.
Let's see why does setState behaves like it does, what problems it can arise and how can we avoid them.
The Why?
It get's really important to know why is setState asynchronous, because we would like to avoid asynchronous codes as much as possible as it is prone to bugs. If we look at it at first, setState doesn't seem like it does any asynchronous task.
const Idunno = () =>{
const [state, setState] = useState<number>(0);
const changeState = () => {
const newState = Math.floor(Math.random() * 10);
setState(newState);
}
return (
<button onClick={changeState}></button>
);
}
Just changing the value of state
what can be asynchronous in that, but for the sake of optimisation, whenever React observes an state change, it schedules an update for a batch of multiple state calls, where it updates all the states at once.
This results in the following condition :
. . .
const changeState = () => {
const newState = Math.floor(Math.random() * 10);
setState(newState);
console.log(state) // prints the previous value of state instead of the latest one
}
. . .
Problems & Solutions
- Race condition
If you have multiple setState
calls it can be prone to some synchronisations issues which may lead to inconsistencies in the expected results.
. . .
const [state, setState] = useState<number>(0);
const changeState = () => {
setState(state+1);
setState(state+1); // may not be consistent
}
. . .
To avoid such issues you can use the callback form of setState
where it provides the value of previous state.
. . .
const [state, setState] = useState<number>(0);
const changeState = () => {
setState((prevState)=>prevState+1);
setState((prevState)=>prevState+1); // works as expected
}
. . .
- Batching
As mentioned above React batches multiple setState
calls for a later update, that's why you should not use the state
's value immediately after calling setSate
, but if in some case you need to use it immediately use the callback provided by setState
.
. . .
const [state, setState] = useState<number>(0);
const changeState = () => {
setState((prevState)=>prevState+1, ()=>{
console.log(state); // guaranteed updated value
});
}
. . .
That's it for this one, see you later with some another topic to discuss upon.