我一直在玩React并制作了一个简单的待办事项列表应用程序来探索一些概念。在测试我的代码时,我意识到在变异/更新状态存储集合时似乎不需要从setState()
返回新状态。
根据其他几个StackOverflow问题和React文档,我编写的代码要么不起作用,要么产生意外结果。但是,在我的所有测试中,代码确实起作用,并且比建议的变异集合方法更具可读性和简洁性。
为什么我的代码有效,以及将它用作模式的危险是什么?
我已经创建了两个版本的应用。一个使用方法来更新所提到的集合in this question,另一个使用setState()
来返回一个空对象。我已将应用程序的完整版本上传到CodePen,因此您可以看到它们的行为相同。
这是原始的关键代码段。我正在使用setState()的回调来保证状态读取和状态写入自动发生(如果调用之间发生异步状态更改)。
addTask(taskText) {
this.setState(prevState => {
const updatedTasks = prevState.tasks;
updatedTasks.set(prevState.uid, taskText); // use the original, un-incremented uid
return {
uid: ++prevState.uid, // increment prev uid and store it as new state
tasks: updatedTasks, // set tasks to updated tasks
}
}, this.logState);
}
但是,对于集合(或任何其他引用值对象),似乎不需要返回实际更改的状态。由于以下代码也有效。唯一的区别是我将uid提升为类属性并且没有费心返回更新的地图。
addTask(taskText) {
this.setState(prevState => {
const updatedTasks = prevState.tasks;
updatedTasks.set(this.uid, taskText); // use the original, un-incremented uid
this.uid++;
return {
// return an empty object
//uid: ++prevState.uid, // update uid to one more than prev uid
//tasks: updatedTasks, // set tasks to updated tasks
}
}, this.logState);
}
如果我不关心将箭头函数中的变异包装为异步防护,那么该方法可以进一步简化:
addTask(taskText) {
this.state.tasks.set(this.uid, taskText); // use the original, un-incremented uid
this.uid++;
this.setState({});
}
似乎唯一真正重要的是setState()
调用本身。我最初的理解是React非常有效,并且onyl检查状态变化,它被告知。但是,最后一个示例似乎表明setState()
导致React检查所有已知状态以进行任何更改(无论它们如何到达),然后相应地更新/呈现。
对原语的更改仍然需要包含在返回的setState()对象中,但对于对象(或至少是Maps),消除复杂性和仅使用裸setState()调用的缺点是什么?
我正在使用以下内容:
答案 0 :(得分:1)
1)React setState
很聪明地识别状态对象实体发生的变化。因此,如果您认为this.setState({})
将this.state = {}
作为空对象,那么它就是错误的。它将做的是比较已经完成的对象实体的更改,它只会更新这些更改。
2)根据反应docs:
永远不要直接改变this.state,因为之后调用setState()可能会替换你所做的突变。将this.state视为不可变。
所以这意味着你可以改变状态,如果你有使用set函数的地图值,所以它不会给你错误,但如果你尝试像this.state.tasks = []
那样初始化它,那么肯定会给出错误。因此,由于this.state
是可变的,但我们不需要直接更改错误,因此您不会产生错误的期望。此外,有时它可能会在以后的this.setState
来电中设置不正确的值。
3)因此,如果您使用set
Map
函数设置州状态,则状态将被操纵但页面不会重新呈现它应该在更新时重新呈现。所以你不应该像这样改变状态,而应该使用this.setState
函数来避免不必要的事情发生。
4)你正在使用removeTasks
prevState
回调this.setState
,所以做出反应的是它会使用prevState,如果你返回空白对象那么它会初始化整个反对它已定义的初始状态。因此,如果您想尝试,可以将initial state
设置为this.state = {tasks: new Map().set(1, "awdawd")}
,然后在点击删除任务按钮时看到您会收到意外结果。因此,如果返回空对象,则react返回初始状态。
仅供参考,您可以在以下链接中详细了解setState
:faq-state