// 函数方式更新
const handleSubmit = (title) => {
setTodoList((currentTodoList) => [
{ title, status: new Date().toDateString() },
...currentTodoList,
]);
};
// 使用数组副本更新
const handleSubmit = (title) => {
let list = [...todoList];
list.unshift({ title, status: new Date().toDateString() });
setTodoList(list);
};
函数式更新
解释:
1:函数式更新:你传递给 setTodoList 的不是一个新的数组,而是一个 更新函数,它接受 currentTodoList(当前的 todoList)作为参数,并返回一个新的数组
2: 这种方式 确保了正确的状态更新,特别是在有多个 setState 调用时,React 会确保你的更新是基于最新的状态进行的;
优点:
1: 可靠性:React 状态更新是异步的,使用函数式更新可以确保你每次都能使用最新的状态值,避免了因 todoList 状态的异步更新而产生的问题
2: 性能:React 会为每个状态更新创建一个新的引用,这样可以确保 React 正确地重新渲染组件。
适用场景:
1: 当你需要依赖当前的状态来计算新状态时,使用这种方式是最安全的。
2: 当你在函数内部访问的是当前的最新状态,并且可能有多个状态更新并发执行时,函数式更新可以避免不必要的错误
第二种写法:使用 数组副本
解释:
1:你首先通过 let list = [...todoList] 创建了 todoList 的副本,然后对副本进行修改,最后通过 setTodoList(list) 更新状态。
2: 浅拷贝:[...todoList] 使用展开运算符创建了 todoList 数组的浅拷贝,因此你修改的是副本,而不是原始的 todoList 数组。
优点:
1: 直观:这种方式比较直接,特别适用于简单的更新逻辑,代码简洁。
2: 适用于简单场景:当你确认状态更新没有其他依赖时,这种方式能快速且容易理解。
缺点:
1: 异步更新问题:由于 React 的 setState 是异步的,如果你有多个并发的状态更新,todoList 的值可能并不是最新的,这可能导致状态不一致。
2 举例:假如你在 todoList 更新的同时还调用了 setTodoList,更新的 todoList 并不总是最新的。多个 setState 操作可能会混淆状态。
适用场景:
适合简单的状态更新,且确认状态更新之间没有竞态条件(例如,多个 setTodoList 调用之间没有依赖关系)时。
区别总结
最佳实践
1:函数式更新 是 React 推荐的方式,尤其是在依赖于当前状态的更新场景下,它能确保状态更新的正确性和一致性,避免竞态条件。
2: 浅拷贝 方式可以在简单的场景中使用,但如果在复杂的状态管理中使用可能会带来问题,特别是在有多个并发更新的情况下。
所以,通常情况下,推荐使用 函数式更新,尤其是在需要依赖当前状态的复杂场景中。