如何在对象数组中使用 usestate 更新状态?
Posted
技术标签:
【中文标题】如何在对象数组中使用 usestate 更新状态?【英文标题】:How to update state with usestate in an array of objects? 【发布时间】:2021-12-14 15:08:59 【问题描述】:我在使用 React useState 挂钩时遇到了一些问题。我有一个带有复选框按钮的 todolist,我想将 'done' 属性更新为 'true',它的 id 与 'clicked' 复选框按钮的 id 相同。如果我 console.log 我的 'toggleDone' 函数,它会返回正确的 id。但我不知道如何更新正确的属性。
当前状态:
const App = () =>
const [state, setState] = useState(
todos:
[
id: 1,
title: 'take out trash',
done: false
,
id: 2,
title: 'wife to dinner',
done: false
,
id: 3,
title: 'make react app',
done: false
,
]
)
const toggleDone = (id) =>
console.log(id);
return (
<div className="App">
<Todos todos=state.todos toggleDone=toggleDone/>
</div>
);
我想要的更新状态:
const App = () =>
const [state, setState] = useState(
todos:
[
id: 1,
title: 'take out trash',
done: false
,
id: 2,
title: 'wife to dinner',
done: false
,
id: 3,
title: 'make react app',
done: true // if I checked this checkbox.
,
]
)
【问题讨论】:
如果您提供尝试设置状态的方式会有所帮助。 您需要使用修改后的状态调用setState()
。你尝试过什么吗?如果有,结果如何?如果您还在为如何开始而苦苦挣扎,请查看map()
函数。
这能回答你的问题吗? Whats the best way to update an object in an array in ReactJS?
此外,使用钩子,无需将数组嵌套在对象中。可以多次调用useState
,分别管理不同的状态值。
【参考方案1】:
D.史密斯的回答很棒,但可以重构为更具声明性,就像这样..
const toggleDone = (id) =>
console.log(id);
setState(state =>
// loop over the todos list and find the provided id.
return state.todos.map(item =>
//gets everything that was already in item, and updates "done"
//else returns unmodified item
return item.id === id ? ...item, done: !item.done : item
)
); // set state to new object with updated list
【讨论】:
【参考方案2】:所有很棒的答案,但我会这样做
setState(prevState =>
...prevState,
todos: [...prevState.todos, newObj]
)
这将安全地更新状态。还将保持数据完整性。这也将解决更新时的数据一致性问题。
如果你想做任何条件,就这样做
setState(prevState =>
if(condition)
return
...prevState,
todos: [...prevState.todos, newObj]
else
return prevState
)
【讨论】:
我将在哪里检查条件,例如当条件为真时更新嵌套项,其他情况相同【参考方案3】:您需要像这样使用spread operator:
const toggleDone = (id) =>
let newState = [...state];
newState[index].done = true;
setState(newState])
【讨论】:
This also mutates the current state, which is an anti-pattern in React.【参考方案4】:我会使用 useState 而不是另一个状态来创建 todos 数组,关键是创建 todos 数组的副本,更新它,并将其设置为新数组。 这是一个工作示例:https://codesandbox.io/s/competent-bogdan-kn22e?file=/src/App.js
const App = () =>
const [todos, setTodos] = useState([
id: 1,
title: "take out trash",
done: false
,
id: 2,
title: "wife to dinner",
done: false
,
id: 3,
title: "make react app",
done: false
]);
const toggleDone = (e, item) =>
const indexToUpdate = todos.findIndex((todo) => todo.id === item.id);
const updatedTodos = [...todos]; // creates a copy of the array
updatedTodos[indexToUpdate].done = !item.done;
setTodos(updatedTodos);
;
【讨论】:
【参考方案5】:const toggleDone = (id) =>
console.log(id);
// copy old state
const newState = ...state, todos: [...state.todos];
// change value
const matchingIndex = newState.todos.findIndex((item) => item.id == id);
if (matchingIndex !== -1)
newState.todos[matchingIndex] =
...newState.todos[matchingIndex],
done: !newState.todos[matchingIndex].done
// set new state
setState(newState);
【讨论】:
【参考方案6】:您可以安全地使用 javascript 的数组映射功能,因为它不会修改现有的状态,这是 react 不喜欢的,它会返回一个新数组。该过程是循环遍历状态的数组并找到正确的 id。更新 done
布尔值。然后使用更新的列表设置状态。
const toggleDone = (id) =>
console.log(id);
// loop over the todos list and find the provided id.
let updatedList = state.todos.map(item =>
if (item.id == id)
return ...item, done: !item.done; //gets everything that was already in item, and updates "done"
return item; // else return unmodified item
);
setState(todos: updatedList); // set state to new object with updated list
编辑:更新代码以切换 item.done
而不是将其设置为 true。
【讨论】:
非常感谢,这正是我想要的! 没问题!我很高兴它有所帮助。 我想在@bravemaster 发布的答案中指出一些事情。他们使用...state, todos: [...state.todos]
传播状态,这是一种很好的做法。使用我的解决方案,如果您要在状态中包含除todos
以外的任何内容,那么它将在setState
操作中丢失(它现在可以正常工作,因为您在状态中拥有的只是 todos 对象)。同时保持所有其他状态和更新待办事项的方法是setState(...state, todos: updatedList);
以上是关于如何在对象数组中使用 usestate 更新状态?的主要内容,如果未能解决你的问题,请参考以下文章
使用 prevState 和 useState 钩子更新对象内部数组中的对象
在 useEffect 中使用 useState 值而不使状态更新 useEffect
React Hooks:使用useState更新状态不会立即更新状态[重复]