[英]How to update state of nested array of array of objects without mutating
[英]How can I update this state without mutating the array?
我想知道如何在以下情況下不改變狀態:
我正在使用 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;
});
};
更新:我看過你們推薦的帖子。 這是一個解決方案,盡管我想知道是否有更簡潔的表達方式:
const toggle = index => {
setTodos(
todos.map((todo, i) =>
i === index ? { ...todo, completed: !todo.completed } : todo
)
);
};
更新2:謝謝大家。 我在下面發布了解決方案。 StackOverflow 不會讓我接受 2 天,但我會這樣做。 建議帖子上接受的答案建議使用第三方庫,並且它不使用 React Hooks,所以我不認為這個問題是重復的。
找到了解決辦法。 map
將返回一個不同的數組,您可以收聽 index 參數(在我下面的代碼片段中,這是i
)以收聽當前索引並更改您的目標對象。
const toggle = index => {
setTodos(
todos.map((todo, i) =>
i === index ? { ...todo, completed: !todo.completed } : todo
)
);
};
這些線程幫助我拼湊起來:
感謝所有幫助過的人。
沒錯,避免這個問題最簡單的方法就是避免變異數據。
一種方法是使用函數式方法,特別是使用非變異數組方法,如.map
:
function TodoList() { const [todos, setTodos] = React.useState([{ text: "Example", completed: true }]); const toggle = index => { const updatedTodos = todos.map((todo, i) => { if (i === index) { todo.completed = !todo.completed; } return todo; }); setTodos(updatedTodos); }; return ( <React.Fragment > {todos.map((item, index) => ( <p key={item.text} onClick={() => toggle(index)} className={item.completed ? "completed" : ""} > {item.text} </p> ))} </React.Fragment> ); } ReactDOM.render( < TodoList / > , document.getElementById('root'))
.completed { text-decoration: line-through; }
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script> <div id="root"></div>
const toggle = (index) => {
const oldTodo = todos[index];
setTodos(oldState => {
oldState.splice(index, 1, {...oldTodo, checked: !oldTodo.checked})
return oldState
})
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.