Setting React Hooks states in a sync-like manner?

problemsolving, programming, react, selfnote


Note 📝 to self...

When you have more than one states defined using useState and need to access updated state value sync-like manner...

I've asked a question in r/reactjs about emulating a callback of setState, which enables you to access updated state value.

Shawn "swyx" Wang posted React Hooks setState Gotcha, which addressed the same problem.

The problem is that useState is an async method just like setState so that when you try to access a state value as shown below,


Buggy 🐛 one

Try it on CodeSandbox.

message value will always contain a number differ by 1 from count.

Count & message off by one

First Workaround

The first workaround was to use useEffect to update the message.



But Dan "gaeron" Abramov has pointed out that

This is unnecessary. Why add extra work like running an effect when you already know the next value? Instead, the recommended solution is to either use one variable instead of two (since one can be calculated from the other one, it seems), or to calculate next value first and update them both using it together. Or, if you're ready to make the jump, useReducer helps avoid these pitfalls.

Dan "gaeron" Abramov

The gist is that, don't store calculated values.

2nd Workaround

The point of the Shawn & my problem was that we need to access the updated state value (kind of like in callback of setState).

So I ended up created my own hook (don't call it a "custom hook"), useAsyncState to mitigate the issue.



Try it on CodeSandbox.

I am using a promise, not accepting a callback as it makes code clunky possibly causing a callback hell.

And also with a setter promise, you can also use async/await syntax.

Update on August 20, 2020

tfiechowski on GitHub kindly suggested a better implementation on GitHub gist.

Thank you, tfiechowski~

1function increment() {2  const newCount = count + 1;3  setCount(newCount);4  setMessage(`count is ${newCount}`);5}6function decrement() {7  const newCount = count - 1;8  setCount(newCount);9  setMessage(`count is ${newCount}`);10}

The count has been updated as newCount once, and the cached value is applied to both count and message.

You can see that I misunderstood Dan's suggestion and tfiechowski corrected it nicely.

_Photo by Martino Pietropoli on _Unsplash