I know a lot of people call it asynchronous and it might be a useful mental model but it does obscure the truth. The state setter function is not asynchronous, it’s not like it returns a promise.
Your example is in fact a “render issue” and not because the state setter function is asynchronous. You do not see the new value logged because React hasn’t re-rendered yet, not because the state setter is asynchronous. On the next render, the state is the new value, not on the current render.
If you want to log the state as it changes useEffect
can be handy but you can often also just log the value you are setting (not the state, but the actual value you are setting).
const nextCount = count + 1;
setCount(nextCount);
console.log(count); // 0
console.log(nextCount); // 1
useEffect(() => {
console.log(count);
}, [count])
React docs: I’ve updated the state, but logging gives me the old value
I would caution against comparing useEffect
to the old lifecycle methods. That usually gets people into trouble.
Just because you can do something with useEffect
doesn’t mean you should. It is (rightly so I’d say) one of the more criticized React hooks and its API is dangerously close to being a footgun.
I’m not saying it doesn’t have its uses but its main purpose is synchronization with external systems. The new React docs are a lot clearer about its use and pitfalls. I don’t think they did a very good job of teaching the hook initially which is one of the reasons why it is often “misused”.
To quote the docs I linked to directly.
Effects are an escape hatch from the React paradigm. They let you “step outside” of React and synchronize your components with some external system like a non-React widget, network, or the browser DOM. If there is no external system involved (for example, if you want to update a component’s state when some props or state change), you shouldn’t need an Effect. Removing unnecessary Effects will make your code easier to follow, faster to run, and less error-prone.