In react 17, if code execution starts inside of react (eg, an onClick
listener or a useEffect
), then react can be sure that after you’ve done all your state-setting, execution will return to react and it can continue from there. So for these cases, it can let code execution continue, wait for the return, and then synchronously do a single render.
But if code execution starts randomly (eg, in a setTimeout
, or by resolving a promise), then code isn’t going to return to react when you’re done. So from react’s perspective, it was quietly sleeping and then you call setState
, forcing react to be like “ahhh! they’re setting state! I’d better render”. There are async ways that react could wait to see if you’re doing anything more (eg, a timeout 0 or a microtask), but there isn’t a synchronous way for react to know when you’re done.
You can tell react to batch multiple changes by using unstable_batchedUpdates
:
import { unstable_batchedUpdates } from "react-dom";
const handleClickAsync = () => {
setTimeout(() => {
unstable_batchedUpdates(() => {
setValue("two");
setIsCondition(true);
setNumber(2);
});
});
};
In version 18 this isn’t necessary, since the changes they’ve made to rendering for concurrent rendering make batching work for all cases.