React useState Functional Updates and Why They're Important

Photo by Joan Gamell on Unsplash

React useState Functional Updates and Why They're Important

Make sure your state is never stale by using a functional update

·

2 min read

One of the ways to manage state in React is with the useState hook.

It's a function that returns a value (the state) and a function to update the value.

Let's take a look at how that works.

React useState: Basic Update

function MyComponent() {
  const [count, setCount] = useState(0);
  const onClick = () => setCount(count + 1);

  return <button onClick={onClick}>{count}</button>;
}

In the example above, the state we're managing is a counter. The button displays the current value of the count and clicking on that button increments the count by 1.

Easy enough.

But there's a subtle problem with the example above.

When the button is clicked and we update the count, it depends on the current value of count. Intuitively this makes sense, but the problem has to do with closures. The value count in setCount(count + 1) is defined during the render cycle of the component.

In other words, count is a fixed value.

We could replace the above with setCount(0 + 1) (count = 0) and it wouldn't change things. This means that if I were to call onClick multiple times, the value for count would be the same for every call. If I spam the button multiple times, I'm basically just doing this over and over again: setCount(0 + 1).

React provides a way to get around this by using a functional update when we call setState (setCount in this case).

Here's how it works.

React useState: Functional Update

function MyComponent() {
  const [count, setCount] = useState(0);
  const onClick = () => setCount(prevCount => prevCount + 1);

  return <button onClick={onClick}>{count}</button>;
}

The difference in this example is that when we call setCount, we pass it a function. When you pass a function to a state updater, React will guarantee that the function is called with the most recent state. If I were to call onClick multiple times this time, React would call the functional updater at some later point and make sure the correct count is used.

The difference is that React is guaranteed to use the latest version of the state when it calls the function.

Demo

Here's an example of the above two use cases. The bad way uses the basic update syntax and the good way uses the functional update syntax.

Try spamming the buttons and see what happens.