简体   繁体   中英

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

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!

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().

You can use unstable_batchedUpdates (from ReactDOM) to get the behaviour you expect.

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

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});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM