Question presented to candidate:
"Why can you not just push to an array in state and call the setter with the same array?"
What a strong answer should cover:
- React decides whether state changed by comparing the reference with
Object.is— a shallow, identity check, never a deep one. - Mutating in place leaves the reference identical, so React sees no change and bails out of re-rendering entirely.
- The dangerous part is not the missed render, it is that the mutation still happened. The data is now wrong and the screen is stale, and the corruption surfaces later during an unrelated update.
- Why reference comparison: deep-comparing every state value on every update would be prohibitively expensive, and impossible for functions.
- Immutability is also what makes
React.memo,useMemodependency arrays, and context value comparison work at all — they are all reference checks. - It underpins concurrent rendering: React can hold a previous state value and render both, which is impossible if the object was mutated in place.
- The patterns: spread for objects and arrays,
map/filter/toSortedrather thansplice/sort/reverse, and a library like Immer when nesting gets deep. - StrictMode freezes state in development for some cases; the discipline is required regardless.
Clarifying questions expected:
- "Is the state deeply nested? That changes whether I would hand-spread or reach for Immer."
Code / implementation expected: Yes — the mutation that silently does nothing, beside the correct copy.