Capítulo 25 de 116
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.
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.setPosition({ x: e.clientX, y: e.clientY }) — a brand-new object reference tells React "this is different, re-render."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.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.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.const [person, setPerson] = useState({ firstName: 'Barbara', lastName: 'Hepworth' });
function handleFirstNameChange(e) {
setPerson({ ...person, firstName: e.target.value }); // new object, one field overwritten
}
someState.field = value — always call the setter with a new object, even if only one field changed.{ ...obj }) only protects one level of nesting — a genuinely nested update needs a spread at each level you're touching.