Why do my state update not impact another function currently running?

Viewed 37

I am building a piece of code in which I need 2 nested components to update a state variable:

function Test() {
    const [test, setTest] = useState()

    async function wait() {
        await new Promise((res, rej) => setTimeout(res, 1000))
        console.log("after 1s waiting, test is", test)
    }

    async function setTest1() {
        await wait()
        setTest("test1")
    }

    async function setTest2() {
        setTest("test2")
    }

    useEffect(() => console.log("after being updated, test is", test), [test]);

    return (
        <div>
            <div onClick={setTest1}>
                <span onClick={setTest2}>update test</span>
            </div>
        </div>
    )
}

When clicking "update test", setTest2 is called then setTest1 is called. This happens asynchronously: setTest1 might be called before setTest2 ends.

First, setTest2 and setTest1 are called. Then setTest2 updates test, giving it the value "test2". At this point setTest1 is still running. It is waiting 1s before continuing. After 1s, the value of test is undoubtedly "test2". Is it not?...

When logging the value of test after waiting 1s, I would expect the result to be "test2", since the state has already been updated to "test2". However, the result is not "test2", but undefined, as if it had not been updated by setTest2.

This piece of code logs this:

after being updated, test is undefined
after being updated, test is test2
after 1s waiting, test is undefined <--- what ???
after being updated, test is test1

It looks like some kind of scoping mechanism, but I am unable to find an explanation for it.

Why is test's value not test2 when logged in the wait call inside of the setTest1 call?

1 Answers

Because "await" was added to call stack, before new version of "await" created.

  • Step 1 : when you started your app, UseEffect ran first => "after being updated, test is undefined" ,
  • Step 2 : When you clicked => setTest1 and setTest2 were called at same time. But "await" was declared at Step 1, and "test" inside of "await" was undefined, so after setTest2() finished => "after being updated, test is test2" , then setTest1() finished => "after 1s waiting, test is undefined" => "after being updated, test is test1"

If you click again. The result is going to be:

---- after being updated, test is test2.

---- after 1s waiting, test is test1.

---- after being updated, test is test1.

Because "await" is redeclared, and "test" inside of "wait" is "test1".

Hope this make you clear. Some updates for more clear:


Everytime setState is called, new "test" is created with new value (not assign new value to current "test" ), but "await" still refers to old "test" ===> So this is why, you got "undefined" for first click.

Related