useEffect.dev
← Back to lessons

Lesson 6. Get the current state when dealing with async code 1. Update the state based on its current value

Have a look at this example:

Result

I expect to see a growing string, with a new dash - added each second. But what I see is just one dash. It tells me that in the callback passed to setInterval, I don’t get the latest value of string, but why?

The reason has less to do with React and hooks than with JavaScript closures. Remember that React components are functions, and updating a local state triggers a new rendering, therefore another call to this function.

  • Rendering #1: string contains >. We call setInterval so each second, we must update string. This rendering’s version of string.
  • Rendering #2 (triggered by the first call to setString): string contains one dash: ->.

Since we make the call to setInterval in the first rendering, the version of string used is the first rendering version, not the next ones. React hooks can’t do much to help us here; it’s how JavaScript closures work.

Not-working solution

Oh, that one is easy, you may say. You forgot to pass string in the dependencies array, as the second argument of useEffect.

useEffect(() => {
// ...
}, [string])

If we pass string in the dependencies array, the problem is that setInterval will be called each time the component is rerendered, meaning each time we call setString. Not what we want to do.

Using the alternative syntax to update a state

We want to update a local state with a new value derived from the current value. It is a perfect situation where the second syntax offered by setXXX (returned by useState) will help us a lot!

setInterval(() => {
setString((currentString) => '-' + currentString)
}, 1000)

With this syntax, we don’t have to care about getting the latest value of string in the callback we give to setInterval since we are sure that currentString (the first parameter of the callback passed to setString) is up to date.

Here is what this working solution looks like:

Result

This first solution works perfectly when you need to get the latest value to update the state. But what if you need it to trigger another side effect? Let’s see another way to get this latest value.