cbdeveloper
cbdeveloper

Reputation: 31485

React won't batch setState calls inside event handler if there's an await call before or between them

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:

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:

enter image description here

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

Answers (2)

Oleksandr Nechai
Oleksandr Nechai

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

ilia
ilia

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 awaits in between, setStates will fall into different stacks.
I believe this is the main reason why react doesn't batch them.

Upvotes: 2

Related Questions