How to update state based on the previous value?
1. The problem with a regular update
Imagine a component:
function Counter() {
const [count, setCount] = useState(0);
const incrementTwice = () => {
setCount(count + 1);
setCount(count + 1);
};
return <button onClick={incrementTwice}>{count}</button>;
}You expect that on click count will increase by 2...
but in fact, only by 1!
Why this happens
setCount does not update count immediately (it is asynchronous).
Both lines use the old value of count (for example, 0).
React simply queues two identical updates:
"make count = 1".
The result is one update, value 1.
2. The correct way: functional update
React lets you pass a function to setState
that receives the previous value of the state:
setCount(prevCount => prevCount + 1);Now React guarantees that you are working with the up-to-date state, even if there were several updates in a row.
Example with incrementTwice
function Counter() {
const [count, setCount] = useState(0);
const incrementTwice = () => {
setCount(prev => prev + 1);
setCount(prev => prev + 1);
};
return <button onClick={incrementTwice}>{count}</button>;
}Now on click count increases by 2,
because each function receives the latest updated value.
3. Works the same way with objects and arrays
For example:
const [user, setUser] = useState({ name: "Tim", points: 0 });
function addPoint() {
setUser(prev => ({ ...prev, points: prev.points + 1 }));
}Here prev is the previous user state,
and you create a new object based on it.
4. Example with logging (for clarity)
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(prev => {
console.log("Previous value:", prev);
return prev + 1;
});
};
return <button onClick={handleClick}>{count}</button>;
}On every click you will see the actual "previous" value in the console, which React guarantees even with asynchronous updates.
5. Summary
| What you want to do | How to write it |
|---|---|
| Set a fixed value | setCount(10) |
| Increment by 1 | setCount(prev => prev + 1) |
| Add an item to an array | setItems(prev => [...prev, newItem]) |
| Change a field of an object | setUser(prev => ({ ...prev, name: "Alex" })) |
Conclusion
setState(value) simply sets a new state
setState(prev => newValue) updates based on the previous value
This is a safe way to handle:
- several calls in a row,
- asynchronous updates,
- dependent computations (for example, counters, arrays, objects)
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.