Functional State Updates

How to safely update state multiple times in a row by using an updater function.

React4 min readConcept 9 of 46

What it is

Instead of passing a new value directly to a state setter (e.g., setCount(count + 1)), you can pass a function: setCount(prevCount => prevCount + 1).

This function is called an **updater function**. React puts these functions in a queue and runs them in order, passing the returned value of the previous function as the input to the next one.

Why it matters

Because state is a snapshot taken at the time of render, a variable like count does not change during the execution of your event handler.

If count is 0, and you write setCount(count + 1) three times in a row, you are effectively writing setCount(0 + 1) three times. The final result will be 1, not 3.

Using an updater function ensures you are always operating on the absolute latest state, even if multiple updates are queued before the next render.

How it works

When you call setCount(c => c + 1), React adds that function to a queue.

When it's time to process the queue, React takes the current state (say, 0), passes it to the first function (0 => 0 + 1), and takes the result (1).

It then passes that result to the next function in the queue (1 => 1 + 1), resulting in 2. This ensures updates are never lost.

The +3 Bug

Try the broken button. It tries to add 3 by calling the setter three times, but it fails. The fixed button uses an updater function to successfully queue the math.

const [score, setScore] = useState(0);
 
// ❌ Broken: Uses the stale snapshot of 'score'
function addThreeBroken() {
setScore(score + 1); // 0 + 1 = 1
setScore(score + 1); // 0 + 1 = 1
setScore(score + 1); // 0 + 1 = 1
}
 
// ✅ Fixed: Uses an updater function to queue math
function addThreeFixed() {
setScore(s => s + 1); // 0 => 1
setScore(s => s + 1); // 1 => 2
setScore(s => s + 1); // 2 => 3
}
Current Score0

Notice how the Broken button only adds 1, because all three setters read 'score' as the same snapshot value. The Fixed button passes a function, successfully queuing up the math.

Check yourself

Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.

  1. 1If `count` is 0, what is the value of `count` after these three lines execute? `setCount(count + 1); setCount(count + 1); setCount(count + 1);`
  2. 2How do you fix the bug from the previous question so the final result is 3?
  3. 3When is it absolutely necessary to use an updater function?

Remember this

  • State variables do not change during a render; they are static snapshots.
  • Calling setState(value) multiple times in a row will only apply the final call.
  • Pass a function (setState(prev => prev + 1)) to queue updates safely.
  • Always use an updater function if your new state depends on the previous state.

Done with this concept?

Mark it complete to track your progress. No login needed.