Reputation: 31485
I've found this interesting React behavior that I would like to know more about.
Usually React will batch multiple setState()
calls when inside an event handler, right?
But I tested that React won't batch the calls if:
async
function with a an await
call.await
call executes before or between the setState()
calls.
await
runs after the setState()
calls, they are batched as usual.QUESTION:
Do you know what is the reason behind this?
CodeSandbox: https://codesandbox.io/s/eventhandlerawaitso-jsdxs
This is the mockAPI
call
function mockAPI() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("I come from API using an AWAIT call");
},500);
});
}
These are the handlers to test:
function handleClickNormal() {
console.clear();
console.log("Calling 1st setState()");
updateBooleanState(false);
console.log("Calling 2nd setState()");
updateBooleanState(true);
console.log("After 2nd setState()");
}
async function handleClickAwaitBefore() {
console.clear();
// AWAIT CALL RUNS BEFORE THE setState CALLS
const fromAPI = await mockAPI();
console.log(fromAPI);
console.log("Calling 1st setState()");
updateBooleanState(false);
console.log("Calling 2nd setState()");
updateBooleanState(true);
console.log("After 2nd setState()");
}
async function handleClickAwaitAfter() {
console.clear();
console.log("Calling 1st setState()");
updateBooleanState(false);
console.log("Calling 2nd setState()");
updateBooleanState(true);
console.log("After 2nd setState()");
// AWAIT CALL RUNS AFTER THE setState CALLS
const fromAPI = await mockAPI();
console.log(fromAPI);
}
async function handleClickAwaitBetween() {
console.clear();
console.log("Calling 1st setState()");
updateBooleanState(false);
// AWAIT CALL RUNS BETWEEN THE setState CALLS
const fromAPI = await mockAPI();
console.log(fromAPI);
console.log("Calling 2nd setState()");
updateBooleanState(true);
console.log("After 2nd setState()");
}
This is the result:
Comments
We can see that the setState()
calls are batched if there are no await
calls (Click Normal) and if the await
call comes after the setState()
(Click Await After).
And that the setState()
calls are NOT batched if there's an await
call before (Click Await Before) or between the setState()
calls (Click Await Between).
Upvotes: 3
Views: 576
Reputation: 1867
First, let's see what Dan Abramov has to say about this matter:
In current release, they will be batched together if you are inside a React event handler. React batches all setStates done during a React event handler, and applies them just before exiting its own browser event handler.
With current version, several setStates outside of event handlers (e.g. in network responses) will not be batched. So you would get two re-renders in that case.
Hmmm, so we can do setState from React event handler and outside of it. React event handler is the function we pass as a prop to the component.
If we take handleClickAwaitBefore
and rewrite it without async-await we get something like this:
function handleClickAwaitBefore() {
console.clear();
// Here we are in event handler and setState is batched
mockAPI().then(function notEventHandlerAnyMore(){
// Here we are in totally different function and setState is not batched any more
console.log(fromAPI);
console.log("Calling 1st setState()");
updateBooleanState(false);
console.log("Calling 2nd setState()");
updateBooleanState(true);
console.log("After 2nd setState()");
})
}
Upvotes: 0
Reputation: 1333
Async programming is very much about call stacks and event loops.
You can find a lot about it in this video: https://www.youtube.com/watch?v=8aGhZQkoFbQ
So, when you have await
s in between, setState
s will fall into different stacks.
I believe this is the main reason why react doesn't batch them.
Upvotes: 2