Home > Mobile >  Why calling set function of useState hook applies immediately in async functions?
Why calling set function of useState hook applies immediately in async functions?

Time:11-28

I experienced different behaviors when calling multiple set functions of useState hook in a sync and async function.

function Test() {
    console.log('app rendering starts.');
    const [a, setA] = useState(1);
    const [b, setB] = useState(11);
    const updateState = () => {
        console.log('\tupdating a starts.');
        setA(2);
        console.log('\tupdating a ends.');
        console.log('\tupdating b starts.');
        setB(12);
        console.log('\tupdating b ends.');
    };
    console.log('app rendering ends.');
    return (
        <div className="App">
            <div>a is {a}</div>
            <div>b is {b}</div>
            <button onClick={() => {
                console.log('--------------sync click--------------');
                updateState();
            }}>Update State a & b Sync</button>
            <button onClick={() => {
                console.log('--------------async click--------------');
                setTimeout(updateState, 0)
            }}>Update State a & b Async</button>
        </div>
    );
}

both buttons execute same codes, but in a different way.
sync button result:

app rendering starts.
app rendering ends.
--------------sync click--------------
    updating a starts.
    updating a ends.
    updating b starts.
    updating b ends.
app rendering starts.
app rendering ends.

async button result:

app rendering starts.
app rendering ends.
--------------async click--------------
    updating a starts.
app rendering starts.
app rendering ends.
    updating a ends.
    updating b starts.
app rendering starts.
app rendering ends.
    updating b ends.

Is this a desired behavior?
How can I have sync result in an async function?
I could not find any tips about this in official documents.
Any help would be appreciated.
Thanks!

CodePudding user response:

This appears to be a known fact according to Github discussions.

One of the comments, which I thinks is pretty self explanatory:

React currently will batch state updates if they're triggered from within a React-based event, like a button click or input change. It will not batch updates if they're triggered outside of a React event handler, like a setTimeout().

React internally uses unstable_batchedUpdates(). You can pass in a callback function and state updates inside that will be batched. This does not happen automatically for timeouts, promises, and async functions. So they are called from outside the callback and state updates inside them are not batched.

import { unstable_batchedUpdates } from "react-dom";
const updateState = () => {
    unstable_batchedUpdates(() => {
      console.log("\tupdating a starts.");
      setA(2);
      console.log("\tupdating a ends.");
      console.log("\tupdating b starts.");
      setB(12);
      console.log("\tupdating b ends.");
    });
  };

Sandbox Link

CodePudding user response:

You may use single useState() or useReducer() hooks call to update multiple state values by storing them together instead of storing it in separate state slices.

const [values, setValues] = useState({a: 1, b: 11});

setValues({a: 2, b: 12});
  • Related