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

IT技术 javascript reactjs immutability immutable.js
2021-04-12 18:58:36

我已经以“正常”方式构建了我的第一个带有状态存储的 React 应用程序,现在我正在研究使用像Este starterkit 中使用的不可变全局状态

  • 所有商店的状态都保存在一个单一的不可变数据结构中
  • 组件没有状态,但基于 store getter 函数访问其 render() 中的数据
  • 商店也是无状态的,但使用游标改变其域的全局应用程序状态。
  • 顶级应用程序组件侦听状态更改,并重新渲染整个组件树。
  • 组件被实现为“纯”,这意味着它们使用 shouldComponentUpdate 来有效地确定它们可以在重新渲染中被跳过。

它以几种方式简化了应用程序结构:

  • 组件不监听存储,也不将存储数据复制到它们的本地状态。他们只是在每次渲染时获取他们的商店状态。
  • 全局状态始终是整个应用程序的快照,这使得调试和添加诸如撤消琐碎之类的功能变得更加容易。
  • 全局状态似乎简化了同构渲染。

我只阅读了关于在 React 中使用不可变数据的积极方面,并且建议避免组件中的状态,所以我想知道是否有任何缺点。我想一定有,因为否则我不明白为什么它不推荐方式来结构作出react的应用程序。

不变性对我来说是新的,所以如果我开始在复杂的现实世界应用程序中使用这种方法,有什么我应该注意的警告吗?

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

3个回答
  1. 文档。SO 中一些投票最多的问题/咆哮是关于更新嵌套列表项之类的琐碎事情,因为文档更面向熟悉函数式编程的受众,这使得其他人不太容易接近。
  2. 将模型层次结构知识与您的组件分离并非易事。我讨厌this.state.getIn("[parent, child, index]")在组件中执行 a ,因为它增加了模型更改破坏组件代码的可能性。这可以通过可扩展性(更多内容见下文)或通过辅助方法来防止,但您肯定会失去普通 JS 成员的简单性。
  3. 我面临的一个重大挑战是能够序列化和反序列化状态。fromJS 方法支持自定义 revivers,但它们是一个皮塔饼,需要仔细测试和维护。
  4. Record 类型被称为嵌套的衰弱严重阻碍。这是可悲的,因为它允许更容易(相对而言)可扩展性,但只鼓励单级层次结构。您无法轻松地从 JSON 创建嵌套记录。这使得很难将它们与常规的不可变 fromJS 用法混合,除非您使用上述 pita revivers。我有没有提到这是多么令人难过,因为 Records 将模型属性作为第一类成员公开,确保模型完整性和默认值。
  5. 然后是可扩展性。尝试在不可变数据模型周围添加帮助程序以抽象组件中的模型层次结构依赖性,并且您将面临明显的障碍竞赛。反序列化变成了一场激烈的离婚战。你需要和复活者混在一起,如果唱片混在一起,他们会大声喊叫犯规等等。更简单的可扩展性机制将大大有助于使我的组件代码更简洁。
  6. 没有记录在案的最佳实践。尽管这与#1 一致,但我仍然应该提到,缺乏良好的文档会阻止人们选择最好的做事方式。我不确定是否应该选择使用更新程序、forEach 或 setIn(等等)来更新不可变结构。这些方法不会相互交叉引用,不足以让您知道周围有哪些替代方案。
大多数这些论点似乎都是反对使用 Immutable.js 的论点。您可以编写普通的 Javascript 并且仍然注意不要改变任何对象,从而获得实际的不变性。
2021-05-30 18:58:36
您是对的,但是该问题已被标记为 ImmutableJS,并且讨论主要围绕它展开。
2021-06-01 18:58:36
谢谢。我会接受这个作为答案,因为除了显而易见的之外,它还提供了更多的见解。
2021-06-11 18:58:36

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

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

谢谢。让我们希望有一天有人能找到一个解决方案来在 React 开发工具中很好地显示值。
2021-05-22 18:58:36
@0x80 是的,如果您将不可变值转换为普通的 JS 值并将它们传递给 props,它们将更具可读性,但是shouldComponentUpdate在树的更下方,您将失去引用相等性的易用性可能是一个很好的权衡。
2021-05-31 18:58:36
我所知道的优点,这就是我想使用它的原因:) 检查不可变数据对于层次结构更下方的组件是否也是一个问题?我的意思是,如果数据作为道具传递,它不会以更易读的形式显示在那里吗?
2021-06-11 18:58:36

我知道答案已被接受,但我发现主要缺点是您将非纯 JavaScript 对象传递到组件代码中,这意味着在从通过传入的不可变对象读取值时不能使用常规 JavaScript属性访问器props相反,您必须使用 Immutable.js 的 read API这是一个重要的权衡。你不能把你对 Immutable.js 的使用保留在它所属的地方,在商店里;它现在已经在你的整个应用程序中泄漏了。这是你可能会感到疼痛Immutable.js 被下一个流行的 Immutability 库所取代,该库拥有自己的 API 或能够使用本机属性访问器。此外,第三方代码几乎肯定会期待普通的旧 JavaScript 对象,因此在传递它们之前需要转换不可变对象。如果您打算将 Immutable.js 引入现有应用程序,那么您的组件代码中哪些props是不可变的,哪些是 JS 对象将很快变得不清楚,除非您非常自律并提出命名方案或其他一些方法与您在渲染链中传递对象时保持一致。

看看记录。在暴露一流成员和可扩展性方面,它们允许与更传统的 JS 对象有一些相似之处。但他们也有自己的不安全感,请看下面我的回答。
2021-06-15 18:58:36