React Best Practices

Chapter 2 · Practice 3 of 5

Update objects and arrays immutably

From State and data flow

Why it matters

React decides whether to re-render by comparing old and new state with Object.is. If you mutate an object or array in place and pass the same reference to the setter, React may skip the update, and memoized children show stale data.

Always create a new value:

Mutating (avoid)Returns a new value (prefer)
push, index assignmentspread [...prev, x], with
sorttoSorted
reversetoReversed
changing an item in placemap, filter

When the next state depends on the previous one, use the updater form, for example setCount((c) => c + 1), so several updates in one event do not overwrite each other.

Do

  • Use toSorted, toReversed and with instead of sort, reverse and index assignment
  • Use the updater function when the next state depends on the previous

Avoid

  • items.push(x); setItems(items)

Example

TSX
// Add, update and remove without mutation
setTodos((prev) => [...prev, newTodo]);
setTodos((prev) => prev.map((t) => (t.id === id ? { ...t, done: true } : t)));
setTodos((prev) => prev.filter((t) => t.id !== id));