调用setState()来改变集合时,可以在React中返回一个空对象吗?

时间:2018-03-20 22:21:05

标签: javascript reactjs ecmascript-6

背景

我一直在玩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()调用的缺点是什么?

参考

我正在使用以下内容:

  • React和ReactDOM v16.2.0(CodePens上的15.1.0)
  • Babel 6.26.0(阶段2支持类属性)
  • ECMAScript 6

1 个答案:

答案 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返回初始状态。

仅供参考,您可以在以下链接中详细了解setStatefaq-state