Why not just mutate
Many UI libraries detect change by comparing references. If you mutate an object in place, its reference stays the same, so the library thinks nothing changed and skips a render. Immutable updates produce a new reference for anything that changed.
Doing it correctly
The rule is to copy at every level you touch, leaving untouched branches shared.
- Use spread to copy an object and override fields.
- Map over an array to replace one item, returning a new array.
- Never push, splice, or assign into existing state.
A subtle bug is shallow copying the top object but mutating a nested one. You must clone down the path to the changed value, not just the root.
Helpers
Writing nested copies by hand is verbose, so libraries offer a draft based approach. You mutate a draft freely and the library produces a correct immutable result. Structural sharing means unchanged branches keep their old reference, which makes memoized selectors and pure components efficient.
Key idea
Copy every level on the path you change so the reference updates, letting reference comparison drive efficient re renders.