Skip to main content

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:

javascript
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

javascript
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:

javascript
oldState === newState

It 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:

javascript
items.push("new"); // mutating the array setItems(items);

Good:

javascript
setItems([...items, "new"]); // creating a new array

React 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

javascript
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

BehaviorDirect mutationCopying ({ ...obj })
Reference changesNoYes
React sees the changeNoYes
Re-render happensNoYes
PredictabilityBreaksPreserved
DevTools / time travel supportImpossiblePossible

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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.