`setX(prev => prev + 1)` Reads the Latest Value
Updater Functions
When the next state depends on the previous state, pass a function to the setter. React calls it with the latest state.
What you'll learn
- Use the updater form to base new state on previous state
- Stack multiple updates in one event correctly
When the next state depends on the previous state, pass a function instead of a value. React calls it with the latest queued state.
The Two Forms
// Direct value — based on this render's snapshot
setCount(count + 1);
// Updater function — based on the latest queued state
setCount(prev => prev + 1); When the Difference Matters
function handleClick() {
setCount(count + 1); // count is 0 → schedule 1
setCount(count + 1); // count is still 0 → schedule 1
setCount(count + 1); // count is still 0 → schedule 1
}
// Final: 1 vs.
function handleClick() {
setCount(prev => prev + 1); // schedule (prev => prev + 1)
setCount(prev => prev + 1); // schedule again
setCount(prev => prev + 1); // and again
}
// React runs them in order: 0 → 1 → 2 → 3. Final: 3 The updater form sees the latest queued value, not the snapshot from the render where the click happened.
Rule of Thumb
If the new state references the current state — increment, append, toggle — use the updater form. Otherwise either form is fine.
| Pattern | Use updater? |
|---|---|
setCount(prev => prev + 1) | Yes |
setItems(prev => [...prev, item]) | Yes |
setOpen(prev => !prev) | Yes |
setName("Ada") | Direct (no prev) |
setUser(newUser) | Direct |
Updater + Objects
For object state, the updater takes the previous object:
const [user, setUser] = useState({ name: "Ada", count: 0 });
function increment() {
setUser(prev => ({ ...prev, count: prev.count + 1 }));
} The Updater Should Be Pure
It’s a function — keep it free of side effects. Don’t read other state, don’t fetch, don’t log. Just compute the next value.
Up Next
If several setX calls happen in one event, React batches them
into a single re-render.