DEV Community

Mohammed Nadeem Shareef
Mohammed Nadeem Shareef

Posted on

React useState hook is asynchronous!

Hello Developers 👋

I would like to share something I recently got to know, so the background is, in my project I was using useState value right after updating it and I was getting previous value(not updated value) and to my surprise I found out that useState hook is asynchronous

what it is?

Basically, the thing is you don't get update value right after updating state.

What is the work around/solution?

We can use the useEffect hook and add our state in the dependence array, and we will always get the updated value.

Show me the code 🤩🤩🤩

import { useState } from "react";

export default function CountWithoutEffect() {
    const [count, setCount] = useState(0);
    const [doubleCount, setDoubleCount] = useState(count * 2);
    const handleCount = () => {
        setCount(count + 1);
        setDoubleCount(count * 2); // This will not use the latest value of count
    };
    return (
        <div className="App">
            <div>
                <h2>Count Without useEffect</h2>
                <h3>Count: {count}</h3>
                <h3>Count * 2: {doubleCount}</h3>
                <button onClick={handleCount}>Count++</button>
            </div>
        </div>
    );
}

Enter fullscreen mode Exit fullscreen mode
  • Here we have very simple and stright forward component.
  • On button click we are updating two states and one state is dependent on other state.
  • The doubleCount will be one step behind count.
  • Check out the Live Demo

Solving this issue 🧐🧐🧐

This can be easily solve with useEffect hook, let's see the code


import { useState, useEffect } from "react";

export default function CountWithEffect() {
    const [count, setCount] = useState(0);
    const [doubleCount, setDoubleCount] = useState(count * 2);
    const handleCount = () => {
        setCount(count + 1);
    };

    useEffect(() => {
        setDoubleCount(count * 2); // This will always use latest value of count
    }, [count]);

    return (
        <div>
            <h2>Count with useEffect</h2>
            <h3>Count: {count}</h3>
            <h3>Count * 2: {doubleCount}</h3>
            <button onClick={handleCount}>Count++</button>
        </div>
    );
}

Enter fullscreen mode Exit fullscreen mode
  • Here, when ever count changes we are updating doubleCount
  • Check out the live Demo

Closing here 👋👋👋

This is Shareef.
Live demo
GitHub repo of this blog
Stackover flow link for more info
My Portfolio
Twitter ShareefBhai99
Linkedin
My other Blogs

Top comments (24)

Collapse
 
froxx93 profile image
Froxx93

In most cases you don't even need to make doubleCount a state.
You can simply say const doubleCount = count * 2; and it'll just calculate it new when count updates. Sometimes, when you have a more complex setup, useEffect might come in handy though.

Collapse
 
shareef profile image
Mohammed Nadeem Shareef

Thanks for the info man... 😊

Collapse
 
eliya profile image
Eliya • Edited

Another (bit-tricky) way to solve this problem, is by setting the 'count' state with a callback, which returns an IIFE by itself, that will take the result of 'count' incremental as argument, sets doubleCount, and returns back the argument, which is the finally returned value for setCound.

Collapse
 
rishi369 profile image
Rishi
Collapse
 
terro216 profile image
ilyamed

Thanks! It was really helpful

Collapse
 
shareef profile image
Mohammed Nadeem Shareef

Hey man, thanks for the info.

Collapse
 
gabrielfmpinheiro profile image
Gabriel Pinheiro

Can I use async/ await too?

Collapse
 
shareef profile image
Mohammed Nadeem Shareef

tbh
I don't know exactly may be you can or can't.
I would prefer not to use it.

Collapse
 
simonxcode profile image
Simon Xiong

Thanks for the writeup. Recently had to complete a project and ran into this issue. Now I at least know how to resolve it in future cases. 👍

Collapse
 
shareef profile image
Mohammed Nadeem Shareef

😄

Collapse
 
diegoo11 profile image
Diegoo11

Muchas gracias, fue muy util.
Thank you very much, it was very useful.

Collapse
 
noyan profile image
noyan • Edited

The doubleCount will be one step behind, because state is just a normal number in each call.
So, if you call setCount (count*2) after setCount(count+1), Counter displays 0 every time.
livedemo: codesandbox.io/s/vigorous-antonell...
see: overreacted.io/a-complete-guide-to...

Collapse
 
shareef profile image
Mohammed Nadeem Shareef • Edited

Yeah! absolutely correct... and that's why I use two diffrent states

my scenario was


setState(state); // Update some state
doSomething(); // Do something with the state

Enter fullscreen mode Exit fullscreen mode

and I was not getting the updated state. as you can see in the example
and two solve the I use useEffect.

setState(state); // Updates the state
useEffect(() => {
    doSomething(state) // do something with the updated state.
},[state]) // This will call again when the state is updated
Enter fullscreen mode Exit fullscreen mode
Collapse
 
noyan profile image
noyan

I realized my comment was already included in your post. Sorry about that.

Actually, there is a simple way to write it without useEffect: setState(prev => prev + 1). It will update properly even if you update it multiple times in one render.

const Func = () => {
  const [count, setCount] = useState(0);    // initially count = 0 (When it was first mounted)
  const handleClick = () => {
    setCount(prev => prev + 1);                 // count = 1 
    setCount(prev => prev + 1);                 // count = 2
  }

  return (
    <button onClick={handleClick}>{count}</button>
)
}
Enter fullscreen mode Exit fullscreen mode

It is called functional update. This one is easier to use or understand, and I recommend to use it when needed.
reactjs.org/docs/hooks-reference.h...

Thread Thread
 
shareef profile image
Mohammed Nadeem Shareef

Yeah! I will definitely try it...

Collapse
 
jiwei_he_de51ab3542e8df9d profile image
Jiwei He • Edited

I modified your code to make it work in the expected way:

codesandbox.io/s/red-field-isi29y?...

Collapse
 
oreakinwole profile image
Ore Akinwole

This is really good

Collapse
 
abdullah565 profile image
abdullah565

Thanks for this article.
But I wanna add here useState is not async. It is actually sync.

why it doesn't update immediately because of the closure.
more on this here: youtube.com/watch?v=RAJD4KpX8LA

thanks :)

PS: i'm still learning it how it does work and how react render works under the hood.

Collapse
 
shareef profile image
Mohammed Nadeem Shareef

Thanks for the input.

Collapse
 
mightymit profile image
Mithun Varghese

So recently I learnt this the hard way too. But what I realized is if you await setCount call, although it doesnot return a promise, the following code will execute after the count has incremented. Hope it helps :)

Collapse
 
shareef profile image
Mohammed Nadeem Shareef

Thanks for the info. I would love to read the hard way. It might help many of us.

Collapse
 
lucilag profile image
Lucila Gaudio

I came to the same solution, I I think this is why: developer.mozilla.org/en-US/docs/W...

Collapse
 
mahikaushi65741 profile image
mahi kaushik

"Great article! Understanding the asynchronous behavior of the useState hook in React is essential for writing efficient and effective code. While it can be easy to assume that the useState hook works synchronously like other functions, it's important to remember that React's state updates are batched and processed asynchronously for performance reasons.

The key takeaway from your article is that relying on the current value of state immediately after calling useState may not always return the most up-to-date value. Instead, you should use the callback function syntax to update state based on its previous value.

For those interested in learning more about React's useState hook, I recommend checking out this React usestate blog. Thanks for sharing your insights!"

Collapse
 
abdulazeem4 profile image
Azeem • Edited

Shareef Bacha