Capítulo 25 de 116

Chapter 25: Updating Objects in State

Core Idea

Treat any object stored in state as read-only — mutating its fields directly doesn't trigger a re-render and corrupts the assumption React relies on for change detection, so every update must instead produce a new object (via spread syntax) and pass that to the setter.

Key Concepts

  • Mutation vs. replacement: changing a value in place (position.x = e.clientX) is a mutation. Even though it technically changes the data, React doesn't know a change happened — it compares object identity, not deep contents, so the screen doesn't update and future renders may show stale or inconsistent state.
  • Fix: create a new object and call the setter: setPosition({ x: e.clientX, y: e.clientY }) — a brand-new object reference tells React "this is different, re-render."
  • Spread syntax for partial updates: setPerson({ ...person, firstName: e.target.value }) copies every existing field, then overwrites just the one that changed — without spread, only the explicitly listed field would survive.
  • Nested objects need spread at every level being changed: person.address.city = 'x' is still a mutation even if the outer setPerson({ ...person }) looks like a copy — a shallow spread only copies one level deep, so updating a nested field requires spreading at each nested level ({ ...person, address: { ...person.address, city: 'x' } }) or restructuring to avoid deep nesting in the first place.
  • Immer (useImmer, a popular convention referenced by the docs) lets you write mutation-looking syntax (draft.address.city = 'x') that's translated into a proper immutable update under the hood — useful when spread-syntax nesting gets unwieldy, without abandoning the read-only-state rule itself.

Code Examples

const [person, setPerson] = useState({ firstName: 'Barbara', lastName: 'Hepworth' });

function handleFirstNameChange(e) {
  setPerson({ ...person, firstName: e.target.value }); // new object, one field overwritten
}
  • What it demonstrates: the spread-and-override pattern for updating a single field of an object in state without mutating the original.

Key Takeaways

  1. Never write someState.field = value — always call the setter with a new object, even if only one field changed.
  2. A shallow spread ({ ...obj }) only protects one level of nesting — a genuinely nested update needs a spread at each level you're touching.
  3. Reach for Immer (or restructure to flatter state) when nested-object update logic becomes hard to read — the read-only rule doesn't go away, but the ergonomics can improve.

Connects To

  • Ch 23 (State as a Snapshot): why React needs a new reference to detect a change at all.
  • Ch 26 (Updating Arrays in State): the same read-only principle applied to arrays instead of objects.