How to update an array of objects in React state

andyrewlee

Andrew Lee

Posted on November 15, 2022

How to update an array of objects in React state

Let’s go over how we can update an array of objects in React state. We have to treat React state as immutable, therefore we have to ensure we create a new copy when mutating the array.

Let’s implementhandleMarkComplete in the following example:

import { useState } from "react";

const List = () => {
  const [todos, setTodos] = useState([
    { id: 1, task: "gym", isComplete: false },
    { id: 2, task: "tan", isComplete: false },
    { id: 3, task: "laundry", isComplete: false },
  ]);

  const handleMarkComplete = (id) => {
    // 1. Find the todo with the provided id
    // 2. Mark the todo as complete
    // 3. Update the todo list with the updated todo
  };

  return (
    <div>
      {todos.map((todo) => (
        <div
          key={todo.id}
          onClick={() => handleMarkComplete(todo.id)}
        >
          {todo.task}
        </div>
      ))}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Let’s write our implementation without the spread operator so we understand what’s going on.

const handleMarkComplete = (id) => {
  // 1. Find the todo with the provided id
  const currentTodoIndex = todos.findIndex((todo) => todo.id === id);
  // 2. Mark the todo as complete
  const updatedTodo = Object.assign({}, todos[currentTodoIndex]);
  updatedTodo.isComplete = true;
  // 3. Update the todo list with the updated todo
  const newTodos = todos.slice();
  newTodos[currentTodoIndex] = updatedTodo;
  setTodos(newTodos);
};
Enter fullscreen mode Exit fullscreen mode

Now that we know what’s going on, let’s use the spread operator.

const handleMarkComplete = (id) => {
  // 1. Find the todo with the provided id
  const currentTodoIndex = todos.findIndex((todo) => todo.id === id);
  // 2. Mark the todo as complete
  const updatedTodo = {...todos[currentTodoIndex], isComplete: true};
  // 3. Update the todo list with the updated todo
  const newTodos = [...todos];
  newTodos[currentTodoIndex] = updatedTodo;
  setTodos(newTodos);
};
Enter fullscreen mode Exit fullscreen mode

We can refactor further by combining spread operators with slice.

const handleMarkComplete = (id) => {
  // 1. Find the todo with the provided id
  const currentTodoIndex = todos.findIndex((todo) => todo.id === id);
  // 2. Mark the todo as complete
  const updatedTodo = {...todos[currentTodoIndex], isComplete: true};
  // 3. Update the todo list with the updated todo
  const newTodos = [
    ...todos.slice(0, currentTodoIndex),
    updatedTodo,
    ...todos.slice(currentTodoIndex + 1)
  ];
  setTodos(newTodos);
};
Enter fullscreen mode Exit fullscreen mode

There is an alternate approach we can take by using the map function. This function might look cleaner but it’s not as efficient as our previous example because we are building up the array one item at a time.

const handleMarkComplete = (id) => {
  const newTodos = todos.map((todo) => {
    if (todo.id === id) {
      return {...todo, isComplete: true};
    }
    return todo;
  });

  setTodos(newTodos);
};
Enter fullscreen mode Exit fullscreen mode

Conclusion

The spread operator is syntactic sugar that we should use once we understand what it does underneath the hood. If we need a refresher on how to update objects and arrays in React state, I wrote an article here: Cheat Sheet for Updating Objects and Arrays in React State.

💖 💪 🙅 🚩
andyrewlee
Andrew Lee

Posted on November 15, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related