ReactJS:为啥我不应该改变嵌套状态?

Posted

技术标签:

【中文标题】ReactJS:为啥我不应该改变嵌套状态?【英文标题】:ReactJS: Why shouldn't I mutate nested state?ReactJS:为什么我不应该改变嵌套状态? 【发布时间】:2015-11-14 11:57:09 【问题描述】:

我已经阅读了关于 setState 的 ReactJS 文档。具体来说,这一行:

永远不要直接改变 this.state,因为之后调用 setState() 可能会替换你所做的改变。将 this.state 视为不可变的。

但在我的代码中,我会这样做:

var x = this.state.x;
x.y.z.push("foo"); // z is a list
this.setState(x:x);

此代码似乎可以正常工作并正确更新状态。但是根据文档,我违反了规则。这种方法有什么问题?是否存在性能问题?比赛条件?我会被 Facebook 开发团队骂吗?理想情况下,我想避免使用不变性助手和所有其他疯狂行为并保持简单。

【问题讨论】:

使用 this.forceUpdate() 代替。或者使用 React.addons.update 来达到同样的效果。 现在看来一切正常。但是,这很可能让位于以后难以调试的问题。不变性规则确实是(强烈)鼓励不要让组件访问超出其需要的信息。如果您的组件需要了解 x.y.z,而不是 x 或 x.y,请将 z 置于您的状态 var z = this.state.zthis.setState(z: z.concat(['foo']))(通过 props 初始化 z)。如果外部世界需要知道 z 的变化,就派发一个 change 事件来触发父组件中 x 的更新,或者触发一个更新 store 的动作。 【参考方案1】:

通过直接操作状态,您绕过了 React 的状态管理,这确实违反了 React 范式。 React 的setState 最显着的好处是它会立即触发重新渲染,它将您的 DOM 更改从影子 DOM 合并到文档 DOM 中。因此,您可以在某个处理程序中捕获您需要的所有状态更改,然后在文字对象中构建您想要的状态更改,然后将其传递给setState。这与您上面的示例不太一样。

因此,虽然您提供的代码示例技术上违反了此规则,因为您在通过对状态对象属性的引用进行变异后直接调用 setState,您的更改会立即通过 React 的状态传递管理。因此,一切都会如您所愿。这个想法是您不想养成以这种方式对状态进行许多更改的习惯,最好在新的文字对象或数组中捕获您的预期状态,然后将其设置为状态一次(即,使用通过调用setState,这将触发一次重新渲染。

编辑:为了更明确地回答您的问题,我要补充一点,真正担心的是开发人员会直接在许多不同的地方或方法中操纵状态,无需调用setState,然后在稍后点或其他代码,调用setState 然后想知道为什么他们的渲染没有产生他们预期的结果。由于setState 在已知的托管状态和作为参数传递给setState 的文字对象之间执行了一个对象merge,因此结果可能不是您之前所期望的那样直接操纵状态。

【讨论】:

您有此信息的来源/链接吗?我有兴趣了解调用 setState 如何触发重新渲染。 facebook.github.io/react/docs/… 感谢您的回复。为了澄清,我只在对状态对象进行了所有更改后才调用 setState。关于结果如何不是我所期望的问题——这就是我试图深入研究的问题。结果怎么会出错? 这个想法是你可以改变状态,然后发出一个不包括你以前的突变的setState 调用,并从本质上覆盖它们,然后让自己困惑为什么你没有得到预期的结果。但是,正如我们所讨论的,在您上面的使用模式中,这不太可能发生。【参考方案2】:

原因是您错过了与 this.setState 关联的回调,他们声称您可以通过直接分配给对象来覆盖数据。

此外,通常在 OOP 中(我不知道 JS 获得了多少星...),您将使用 getter 和 setter 方法,而不是直接操作对象。在这个例子中通过this.state.prop1 = "xyz";

我从来没有用 react 覆盖我直接设置的状态属性。以这种方式编写代码的诱惑可能是写入状态而不重新渲染。但如果你这样做,你可能会考虑不把它们放在 state 中。

如果重新渲染性能是个问题,请查看https://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate

【讨论】:

这是正确的^^ 应该注意的是,如果您直接操作状态,那么您必须跟踪哪些数据已经处于状态,这可能会变得乏味。补丁模式更容易维护,这是 setState 函数强制执行的。【参考方案3】:

主要原因是 shouldComponentUpdate 不能正常工作。

【讨论】:

以上是关于ReactJS:为啥我不应该改变嵌套状态?的主要内容,如果未能解决你的问题,请参考以下文章

ReactJS:为啥将组件初始状态传递给一个反模式?

ReactJS 从子组件修改父状态

ReactJS - 如何更新嵌套和“正常”状态属性?

ReactJS:如何使用动态键访问和更新嵌套状态对象?

ReactJS:仅更新嵌套状态对象中的特定字段[重复]

Redux reducers——改变深度嵌套状态