All topics
Statebeginner

Immutability and Why React Cares

Understand why React state updates require new objects/arrays rather than in-place mutation, and how this connects to change detection.

React determines whether a component needs to re-render by comparing the previous and next state (and props) values, and for objects and arrays it does this comparison by reference (Object.is / ===), not by deeply inspecting every field. If you mutate an object in place and pass the same reference back to a setter, React sees the identical reference and may conclude nothing changed, skipping the re-render even though the data 'looks' different.

It's like handing someone a photocopy of a document with your edits already applied (a new reference) versus scribbling directly on their original copy and handing the exact same physical page back — from the receptionist's (React's) point of view checking 'is this the same page I already filed?', the scribbled-on original still looks like the same page.

Key Concepts

1
This is why the idiomatic pattern for updating array or object state is to create a new array/object — using spread syntax, .map(), .filter(), or a library like Immer — rather than calling mutating methods like .push(), .splice(), or directly assigning to a property. The new reference signals to React that something changed and a re-render is warranted.
.map().filter().push().splice()
2
Immutability also has secondary benefits beyond change detection: it makes state easier to reason about (a past state snapshot can never be silently altered later), supports debugging tools like time-travel through Redux devtools, and works well with memoization strategies (React.memo, useMemo) that rely on reference equality checks to decide whether to skip work.
React.memouseMemo
3
Interviewers frequently present a snippet that mutates an array in state directly (items.push(newItem); setItems(items)) and ask why the UI doesn't update — the correct diagnosis is that the setter received the same array reference as before, so React's reference-equality check saw no change.
items.push(newItem); setItems(items)