在 React 中使用不可变状态有什么缺点?

What are disadvantages to using immutable state in React?

我已经用 "normal" 方式构建了我的第一个带有状态存储的 React 应用程序,现在我正在研究使用不可变的全局状态,就像在 Este starterkit 中使用的那样。

它在几个方面简化了应用程序结构:

我只读到有关在 React 中使用不可变数据的正面信息,建议避免在组件中使用状态,所以我想知道是否有任何缺点。我想肯定有,因为否则我不明白为什么不推荐它作为 构建 React 应用程序的方式。

不可变性对我来说是新事物,所以如果我开始在复杂的现实世界应用程序中使用这种方法,是否有任何我应该注意的注意事项?

我能想到的唯一一件小事是使用 forceUpdate(),因为 Este 正在使用它,因为我读到它是一个同步函数。例如,Morarty 似乎将更新推迟到下一个动画帧以便对其进行批处理,但我认为这是一个实现细节/优化,而不是不可变单状态方法的一些继承缺点。

通过 props 传递数据的一个优点是您可以利用 shouldComponentUpdate 进行更高效的更新。如果您假设数据总是来自父级(例如通过 props),您可以有效地防止大部分组件树不得不重新渲染。不可变值使这非常好,因为您可以在 shouldComponentUpdate 组件层次结构中相对较高的位置进行引用相等性检查。

我在 React 中使用不可变数据时发现的唯一其他缺点是 React 开发人员工具不能很好地与它们一起工作(它们向您展示不可变值的底层数据结构,而不是 JS 友好的值)。

我知道答案已经被接受,但我发现主要缺点是您将非普通 JavaScript 对象传递到组件代码中,这意味着您不能使用常规 JavaScript property accessors when reading values from the Immutable objects passed in via props. Instead you have to use Immutable.js' read API。这是一个重要的权衡。您不能将 Immutable.js 的使用保留在它所属的地方,在商店中;它现在已经在你的整个应用程序中泄露了。当 Immutable.js 被下一个拥有自己的 API 或能够使用本机 属性 访问器。此外,第三方代码几乎肯定会期望普通的旧 JavaScript 对象,因此在传递它们之前需要转换不可变对象。如果您计划将 Immutable.js 引入现有应用程序,那么在您的组件代码中很快就会变得不清楚哪些 props 是不可变的,哪些是 JS 对象,除非您非常自律并想出一个命名scheme 或您在渲染链中传递对象时使用的其他方法。

  1. 文档。 SO 中投票最多的一些 questions/rants 与更新嵌套列表项等琐碎的事情有关,所有这些都是因为文档更面向熟悉函数式编程的观众,这使得其他人不太容易接近。
  2. 将模型层次结构知识与您的组件分开并非易事。我讨厌在组件中执行 this.state.getIn("[parent, child, index]"),因为它增加了模型更改破坏组件代码的可能性。这可以通过可扩展性(更多内容见下文)或辅助方法来防止,但你肯定会失去普通 JS 成员的简单性。
  3. 我面临的一个重大挑战是能够序列化和 反序列化状态。 fromJS 方法支持自定义 revivers 但它们是皮塔饼,需要仔细测试和维护。
  4. 记录类型被称为嵌套的弱化严重阻碍。这是可悲的,因为它允许更容易(相对而言)的可扩展性,但只鼓励单级层次结构。您无法从 JSON 轻松创建嵌套记录。这使得很难将它们与常规的不可变 fromJS 用法混合使用,除非您使用上面提到的 pita revivers。我有没有提到这有多可悲,因为 Records 将模型属性公开为第一个 class 成员,确保模型完整性和默认值。
  5. 然后是可扩展性。尝试在不可变数据模型周围添加帮助程序以抽象组件中的模型层次结构依赖关系,你将迎来一场引人注目的跨栏比赛。反序列化变成了一场激烈的离婚大战。你需要和 revivers 打交道,如果 Records 混合在一起,他们会大喊大叫等等。一个更简单的可扩展性机制将大大有助于使我的组件代码更清晰。
  6. 没有记录的最佳实践。尽管这与 #1 相符,但我仍然应该提到,缺乏良好的文档会阻止人们选择最佳的做事方式。我不确定我是否应该选择使用更新程序、forEach 或 setIn(等等)来更新不可变结构。这些方法相互之间的交叉引用不足以让您知道有哪些替代方法。