Я хочу знать, как не изменять состояние в следующем сценарии:
Я использую React Hooks. У меня есть TodoList
, который перечисляет TodoItems
. TodoItems
можно щелкнуть, чтобы запустить функцию с именем toggle
, которая переключает их свойство completed
.
import React, { useState } from "react";
import TodoItem from "./TodoItem";
export default function TodoList() {
const [todos, setTodos] = useState([
{ text: "Example", completed: false }
]);
const toggle = index => {
const newTodos = [...todos];
newTodos[index].completed = !newTodos[index].completed; // Mutating state?
setTodos(newTodos);
};
return (
<>
{todos.map((item, index) => (
<TodoItem key={index} index={index} todo={item} toggle={toggle} />
))}
</>
);
}
Мне сказали, что это состояние мутирования:
Вы создайте копию массива todos, но вы по-прежнему изменяете элемент внутри массива:
newTodos[index].completed = !newTodos[index].completed;
Распространение массива создает только поверхностную копию массива, но исходный объект todo не копируется и, следовательно, мутирует .
Итак, как правильно справиться с этим без изменения состояния? Я попытался передать параметр prevTodos
, но состояние не обновляется:
const toggle = index => {
setTodos(prevTodos => {
prevTodos[index].completed = !prevTodos[index].completed;
return prevTodos;
});
};
Обновление: Я просмотрел сообщения, которые вы все рекомендуется. Это решение, хотя мне интересно, есть ли более чистый способ express это:
const toggle = index => {
setTodos(
todos.map((todo, i) =>
i === index ? { ...todo, completed: !todo.completed } : todo
)
);
};
Обновление 2: Спасибо всем. Я разместил решение ниже. StackOverflow не позволит мне принять его в течение 2 дней, но я сделаю это тогда. Принятый ответ на предложенный пост предполагает использование сторонней библиотеки, и она не использует React Hooks, поэтому я не считаю этот вопрос дубликатом.