Why React State Does Not Update Immediately

React state only changes for the next render, not the line that sets it. Learn why the value looks stale and how snapshots and batching work.

5 min read

React state does not update immediately because setting state only changes it for the next render. The variable you already have keeps its old value until that render happens. React also waits until your event handler finishes before it applies the update, which groups several changes into one pass.

The symptom

A common surprise looks like this: the screen updates, but a log right after the setter shows the old value. The same old value appears even inside a timer that runs much later.

App.jsxApp.jsx
import { useState } from "react";
 
function Counter() {
  const [count, setCount] = useState(0);
  function handleClick() {
    setCount(count + 1);
    console.log(count);
  }
  return <button onClick={handleClick}>Clicked {count} times</button>;
}

The button text moves from 0 to 1, but the log still prints 0. Calling the setter did not change the count variable that this render already created.

State is a snapshot

Each render receives a fixed value for every piece of state. The setter does not mutate that value.

It records the next value and asks React to render again, and only the next render sees the new value. The variable you hold right now is sealed for this render.

Think of state as a photograph of the UI at one moment. The handler can only read the photograph it was given, even if it runs after several seconds. That is why the old value keeps appearing.

A timer makes this visible. If the handler sets a value and then reads it inside a setTimeout, the timer still sees the old snapshot.

The state inside React may have moved on, but the closure kept the value from the render where it was created. The stale value is correct for that closure, even when it looks wrong next to the updated screen.

Batching waits for the handler

React also delays the re-render until all the code in your handler has run. It collects every update, then applies them together. This is called batching, and it is why no part of the screen changes mid-handler.

Think of a waiter writing down a full order before walking to the kitchen. React records every update first, then serves one re-render with the final values, so you never see a partially updated screen. Batching also means one event costs one render, no matter how many set calls it makes.

How to read the latest value

There are three ways to get the value you need without fighting the snapshot.

  • To calculate a next value from the previous one, pass an updater function.
  • To show a value that changes, render it directly instead of reading it in the same handler.
  • To run code after a render, use an Effect only for real synchronization with an external system.

The setter's job is to schedule the next render. The useState guide covers the full behavior if you want the fundamentals again.

For a form, the value on screen always matches the state of the current render, so you rarely need to read a just-set value. The staleness only surprises people who log or inspect state inside the same handler that changed it.

Common mistake

Logging state immediately after setting it is the most common confusion. If you need the value you just computed, store it in a local variable and use that, rather than reading state before the next render.

A second confusion is mutating state and expecting a re-render. An object or array that changes in place still looks identical to React, so it skips the update entirely. Replace the value instead.

What to learn next

Once the snapshot idea clicks, updater functions and batching cover the rest of state timing. From there, immutable updates to objects and arrays keep the snapshot model working in practice. Understanding why the value is stale turns a confusing bug into predictable behavior.

Rune AI

Rune AI

Key Insights

  • Setting state only changes it for the next render.
  • Each render holds a fixed snapshot of every state value.
  • React batches updates until the event handler finishes.
  • Reading state right after the setter returns the old value.
  • Use a local variable or an updater function to read the latest value.
RunePowered by Rune AI

Frequently Asked Questions

Why does console.log show the old state after setState?

The setter only schedules the next render. The variable in the current render keeps its value until that render happens.

Does batching make state updates slower?

No. Batching groups updates so one event triggers one render instead of many. The screen updates right after the handler finishes.

How can I read the value I just computed?

Store it in a local variable and use that, or pass an updater function when the next value depends on the previous one.

Conclusion

Setting state schedules the next render instead of changing the current variable. The value looks stale because each render holds a fixed snapshot, and batching delays the update until the handler ends.