I found that, if:
setStateawaitthen, there is a frame where one state is set but the other is not.
Example:
const [bool1, setBool1] = useState(false)
const [bool2, setBool2] = useState(false)
useEffect(() => {
(async () => {
const _bool1 = await true
setBool1(_bool1)
const _bool2 = true
setBool2(_bool2)
})()
}, [])
console.log(`bool1: ${bool1}, bool2: ${bool2}`)
Live example (open browser console for output):
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script>
<script type="text/babel">
const {useState, useEffect} = React;
const App = () => {
const [bool1, setBool1] = useState(false)
const [bool2, setBool2] = useState(false)
useEffect(() => {
(async () => {
const _bool1 = await true;
setBool1(_bool1);
const _bool2 = true;
setBool2(_bool2);
})();
}, [])
console.log(`bool1: ${bool1}, bool2: ${bool2}`);
return <p>See browser console for output</p>;
}
ReactDOM.render(<App />, document.body);
</script>
The output is:
bool1: false, bool2: false
bool1: true, bool2: false
bool1: true, bool2: true
Here, the second line is confusing - I feel that there shouldn't be a frame where bool1 and bool2 don't have the same value. I know the fact that setState doesn't set a value in the same frame in general, but is that fact actually related in this case?
More confusingly, if 1. is not met, namely, if I remove await for setBool1, the second line vanishes. I don't get how the await breaks the synchronization between setBool1 and setBool2 in this case - because I think the promise is already reasolved when setBool1 is called, so it shouldn't make a difference.
The useState hook triggers re-render. So, since it's not batched due to asynchronous state updates, when setBool1(_bool1) is executed the component re-renders with the new bool1 value and then continues with the next setBool2(_bool2) that is why you see this happening in that order:
bool1: false, bool2: false
bool1: true, bool2: false
bool1: true, bool2: true
A React feature that combines all the state updates into a single update, causing a single re-render thereby improving the performance of the app. In earlier versions of React, batching was only done for the event handlers.
In the case of asynchronous state updates, state updates are not batched.
Here you can find a good and extensive explanation https://blog.saeloun.com/2021/07/22/react-automatic-batching.html
More info: https://github.com/reactwg/react-18/discussions/21
This is supposed to change with React 18
Starting in React 18 with createRoot, all updates will be automatically batched, no matter where they originate from.