State Snapshot Semantics
A render observes a fixed snapshot of state, so changing state does not immediately change values captured by that render. In React, event handlers and JSX created during a render use the state values from that particular render.
React state behaves differently from an ordinary mutable variable. When a component renders, React gives that render a snapshot of the current state values and uses those values to calculate the JSX and event handlers for that render.
Calling a state setter such as `setCount(count + 1)` requests another render; it does not mutate the `count` variable already being used by the current render. Code that continues executing in the current event handler still sees the old snapshot.
This explains why several updates based on the same value can produce only one increment. For example, three calls to `setCount(count + 1)` inside one handler can all calculate the same next value when they originate from the same snapshot.
When a new state value depends on the previous value, use a functional update such as `setCount(c => c + 1)`. React can then apply each updater against the latest queued state value rather than repeatedly deriving the result from one captured snapshot.
The same idea applies to asynchronous callbacks. A callback created during a render can retain the state value from that render even if later renders have occurred. Understanding which render created a callback is therefore important when debugging stale-state behavior.
Key points
- Each render receives its own fixed state snapshot.
- A state setter schedules an update; it does not mutate the current render's state variable.
- Multiple updates based on the same snapshot can calculate the same next state.
- Use functional updates when the next state depends on the previous state.
- Callbacks can observe stale state when they capture values from an older render.
Interview questions
What does it mean to say that React state is a snapshot?
Each render receives state values corresponding to that render. React uses those values to produce the JSX and event handlers for the render. Calling a state setter requests another render rather than changing the variable inside the current render. Therefore, code already executing continues to observe its existing snapshot.
Why doesn't calling `setCount(count + 1)` three times necessarily increment by three?
All three expressions can read the same `count` value from the current render. If that value is `0`, every expression produces `1`, so React receives three requests for the same resulting value. They do not form a chain where the second expression automatically sees the first update. Functional updates solve this because each updater receives the appropriate previous state.
When should you use a functional state update?
Use a functional update when the new state depends on the previous state. For example, `setCount(c => c + 1)` expresses an increment without depending on the value captured by the current render. This is especially important when several updates may be queued or when callbacks can run after the render that created them. It also makes the dependency on prior state explicit.
How can state snapshot semantics cause a stale closure?
A callback created during a render closes over the values visible during that render. If the callback executes later, it can still read those captured values even though the component has rendered again. This is why timers, subscriptions, and asynchronous callbacks can observe older state. The appropriate solution depends on the requirement and can include functional updates, refs, or restructuring the effect or callback.