Capítulo 26 de 116

Chapter 26: Updating Arrays in State

Core Idea

The same read-only rule from Ch 25 applies to arrays in state: never call mutating array methods (push, pop, splice, shift, unshift, direct index assignment, sort/reverse in place) on state — always derive a new array and pass that to the setter.

Key Concepts

  • Mutating vs. non-mutating array operations: push()/splice()/arr[i] = x mutate in place and must be avoided on state arrays; [...arr, item]/filter()/map()/slice() all produce a new array and are the correct replacements.
  • Adding: setList([...list, newItem]) (append) or [newItem, ...list] (prepend) — spread the existing array into a new one alongside the addition.
  • Removing: setList(list.filter(item => item.id !== targetId)) — filter out the item(s) you don't want, rather than splicing the original in place.
  • Transforming every item: setList(list.map(item => transform(item))) — map produces a new array; never mutate an item found via .find() and expect the change to register.
  • Replacing one item: setList(list.map(item => item.id === targetId ? { ...item, changed: true } : item)) — combines map (new array) with spread (new object per Ch 25) for the one item that changes.
  • Inserting at a position: build the new array by slicing before and after the insertion index and spreading the new item between them ([...list.slice(0, i), newItem, ...list.slice(i)]) rather than splice.
  • Arrays of objects need both layers of copying: replacing or updating an object inside an array in state requires map for the array layer and spread for the object layer — mutating a nested object even while "correctly" using map on the outer array still breaks the rule if the item itself is mutated in place.
  • Immer applies here too — useImmer lets nested array-of-objects updates read like direct mutation (draft[i].done = true) while still producing safe, new state under the hood.

Code Examples

// Toggle one todo's `done` field without mutating the array or the item
setTodos(todos.map(todo =>
  todo.id === targetId ? { ...todo, done: !todo.done } : todo
));
  • What it demonstrates: the combined map+spread pattern for updating one object inside an array of objects in state.

Key Takeaways

  1. Memorize the non-mutating equivalents: push→spread-append, splice(remove)→filter, in-place edit→map+spread, splice(insert)→slice-and-spread.
  2. Updating an array of objects always needs two layers of "new-ness" — a new array (via map) and a new object for the one item that actually changed (via spread).
  3. sort() and reverse() mutate in place even though they seem read-only-ish — copy first ([...arr].sort(...)) before calling them on state-derived data.

Connects To

  • Ch 25 (Updating Objects in State): the object-spread technique this chapter combines with map/filter.
  • Ch 17 (Rendering Lists): the key prop these array items need when rendered.