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>
);
}
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);
};
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);
};
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);
};
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);
};
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.
Top comments (0)