Jamie Balfour

Welcome to my personal website.

Find out more about me, my personal projects, reviews, courses and much more here.

Part 4.3Updating objects and arrays in state

Treat state as read-only

Do not mutate an object or array already stored in state. Create a new value and pass it to the setter. This preserves previous render snapshots and gives React a clear signal that the value changed.

Update an array

JSX
setTasks((current) => [
  ...current,
  {
    id: crypto.randomUUID(),
    title: newTitle,
    complete: false
  }
]);

setTasks((current) =>
  current.filter((task) =>
    task.id !== id
  )
);

Use map to replace or transform items, filter to remove them and spread syntax to add them. These methods return new arrays.

Update an object

JSX
setTasks((current) =>
  current.map((task) =>
    task.id === id
      ? {
          ...task,
          complete: !task.complete
        }
      : task
  )
);

The spread is shallow. If nested data must change, copy each object along the path, or consider a flatter state shape.

Why mutation causes trouble

tasks.push(newTask) changes the existing array and returns its length, not a new array. Changing task.complete also edits an object used by an earlier render. New arrays and objects preserve each render's snapshot and make changed references clear.

The toggle example maps every item. It copies the matching object with one changed property and reuses all unchanged objects inside a new array.

Challenge: edit a task title

Write renameTask(id, newTitle) using a functional state update and map. Replace only the matching task and preserve its other properties.

Solution shape
setTasks(current => current.map(task =>
  task.id === id ? { ...task, title: newTitle } : task
));
Feedback 👍
Comments are sent via email to me.