In React, you always hear: *“Don’t mutate state directly.”* Is it because Javascript object are call by reference? Not exactly - the real reason is React can't detect the change if the reference stays the same.
How React check for changes
- Primitives (numbers, strings, booleans) → React compares by value.
- References (objects, arrays) → React compares by reference (memory address).
React doesn't do deep comparison. it only checks if the value or reference changed
Primitive Example
const [count, setCount] = useState(0);
setCount(1); // new value → React sees change → re-render
setCount(1); // same value → React sees no change → skip
👉 Simple: if the value is new, React re-renders. If it’s the same, React skips.
Reference Example
const [user, setUser] = useState({ name: "Andy" });
// ❌ Wrong: mutate
user.name = "Jack";
setUser(user); // same reference → React can’t detect → no re-render
// ✅ Correct: new reference
setUser({ ...user, name: "Jack" }); // new object → React detects change → re-render
👉 For objects/arrays, React only checks the reference. Mutating the old object keeps the same reference, so React thinks nothing changed.
Common Misunderstandings
- “It’s because of call by reference.” Not exactly. The issue is that React only checks reference equality, not the object’s inside.
- “Immutable updates are slow.” They cost a little memory, but guarantee correctness and enable optimizations like
React.memo
Practical Use
- Arrays →
setTodos([...todos, newTodo]) - Objects →
setUser({ ...user, age: 26 })
Wrap-Up
- Primitives → compared by value.
- Objects/arrays → compared by reference, no deep comparison.
- Mutating keeps the same reference → React can’t detect.
- Immutable updates create a new reference → React re-renders correctly.