New object when updating state
Short answer
Because React does not track internal changes to objects; it determines whether the state has changed by the reference to the object.
If you mutate (change) the old object directly, React thinks the state hasn't changed and won't re-render the component.
1. What happens when you do this:
const [user, setUser] = useState({ name: "Alex", age: 25 });
function updateName() {
user.name = "Tim"; // mutating the object directly
setUser(user);
}What React sees:
- Old reference: 0xA12F
- New reference: 0xA12F (the same, since the object was not recreated)
- React compares the references and decides: "It's the same object, so there are no changes."
Result: the component does not re-render; the UI stays old despite the data having changed.
2. The correct way: create a new object
setUser({ ...user, name: "Tim" });What happens now:
- Old object -> 0xA12F
- New object -> 0xB83C (same structure, but a different reference)
- React compares the references and understands: "This is a new object, the component needs to be updated."
The component re-renders, the UI updates.
3. Why React compares references, not values
React uses a shallow comparison to determine whether the component needs to be updated:
oldState === newStateIt doesn't do a deep comparison (deep equal); otherwise it would be too slow for large objects.
That's why React determines a state change by a change in the reference (pointer). If the reference to the object changed, the state is new, and the UI is updated.
4. Example with an array
The same principle applies to arrays:
Bad:
items.push("new"); // mutating the array
setItems(items);Good:
setItems([...items, "new"]); // creating a new arrayReact sees the new reference to the array and updates the component.
5. Why mutation is also a logical error
If you mutate the old state, then:
- old references (in closures, hooks, props, effects) now point to changed data, which breaks predictability,
- React may "think" the state is old and not trigger the necessary effects,
- devtools and time-travel debugging become impossible (you can't "roll back" the state if it has been mutated).
6. A visual example
function UserCard() {
const [user, setUser] = useState({ name: "Alex", age: 25 });
const handleClick = () => {
// Direct mutation
user.age++;
setUser(user); // won't work
};
const handleClickCorrect = () => {
// Immutable update
setUser(prev => ({ ...prev, age: prev.age + 1 }));
};
console.log("Re-render:", user);
return (
<>
<p>{user.name} - {user.age}</p>
<button onClick={handleClick}>Bad</button>
<button onClick={handleClickCorrect}>Good</button>
</>
);
}Clicking "Bad" won't cause React to re-render, while clicking "Good" will update the UI as expected.
Summary
| Behavior | Direct mutation | Copying ({ ...obj }) |
|---|---|---|
| Reference changes | No | Yes |
| React sees the change | No | Yes |
| Re-render happens | No | Yes |
| Predictability | Breaks | Preserved |
| DevTools / time travel support | Impossible | Possible |
Final thought:
In React, state must be immutable. Always create a new object or array instead of changing the old one; that way React can correctly detect that the state has changed and update the interface.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.