CodePark/ questions / State
START PRACTICINGPRACTICE
Frontend question · React › State

Why must you update objects and arrays in state immutably? Show how to update a nested field and add, remove and change array items.

setUser(u => ({ ...u, address: { ...u.address, city: 'Cairo' } }));

setTodos(t => [...t, newTodo]);                    // add
setTodos(t => t.filter(x => x.id !== id));          // remove
setTodos(t => t.map(x => x.id === id ? { ...x, done: true } : x)); // change

You must not mutate state, because React detects changes by comparing references. It compares the old and new value with Object.is. If you change an object and pass the same object back, React sees no change.

Mutation causes these problems:

  • React can skip the re-render, so the screen does not update.
  • Code that compares references sees no change. This includes memo, useMemo dependencies and effect dependencies.
  • It breaks the snapshot model, because older renders now see the changed data.

Instead, create a new object or array that contains the change. Spread the parts you keep and replace the parts you change. Copy each level of nesting you touch. For example, to change city, copy both the user and the address: { ...u, address: { ...u.address, city: 'Cairo' } }.

For arrays, use methods that return a new array:

  • Add: [...arr, item].
  • Remove: filter.
  • Change an item: map.

Avoid push, splice, sort and reverse directly on state, because they change the original array. Copy first, or use toSorted. Immer is a popular helper library. It lets you write code that looks like mutation, and it produces new copies for you.

  • React compares state with Object.is. The same reference means no change.
  • Mutation breaks memo, dependency arrays and the snapshots of older renders.
  • Copy each nested level you change, using spread.
  • For arrays, use spread to add, filter to remove and map to update.
  • Immer can make deeply nested updates simpler.