我是 Redux 的新手 - 我真的想了解使用函数式编程使单向数据更优雅的大局。
在我看来 - 每个减速器都采用旧状态,在不改变旧状态的情况下创建一个新状态,然后将新状态传递给下一个减速器以执行相同的操作。
我知道不会造成副作用有助于我们获得单向数据流的好处。
我真的不明白不改变旧状态的重要性。
我唯一能想到的可能是我读过的“时间旅行”,因为如果你坚持每一个状态,你就可以执行和“撤消”。
问题:
我们不想在每一步都改变旧状态还有其他原因吗?
我是 Redux 的新手 - 我真的想了解使用函数式编程使单向数据更优雅的大局。
在我看来 - 每个减速器都采用旧状态,在不改变旧状态的情况下创建一个新状态,然后将新状态传递给下一个减速器以执行相同的操作。
我知道不会造成副作用有助于我们获得单向数据流的好处。
我真的不明白不改变旧状态的重要性。
我唯一能想到的可能是我读过的“时间旅行”,因为如果你坚持每一个状态,你就可以执行和“撤消”。
问题:
我们不想在每一步都改变旧状态还有其他原因吗?
如果处理得当,使用不可变数据结构会对性能产生积极影响。在 React 的情况下,如果数据没有改变,性能通常是为了避免不必要的应用程序重新渲染。
为此,您需要将应用程序的下一个状态与当前状态进行比较。如果状态不同:重新渲染。否则不要。
要比较状态,您需要比较状态中的对象是否相等。在普通的旧 JavaScript 对象中,您需要深入比较以查看对象内的任何属性是否发生了变化。
对于不可变对象,您不需要它。
immutableObject1 === immutableObject2
基本上可以解决问题。或者,如果您使用的是像 Immutable.js 这样的库Immutable.is(obj1, obj2)
。
在react方面,您可以将其用于shouldComponentUpdate
方法,就像流行的PureRenderMixin
那样。
shouldComponentUpdate(nextProps, nextState) {
return nextState !== this.state;
}
当状态没有改变时,这个函数可以防止重新渲染。
我希望,这有助于不可变对象背后的推理。
“无突变”咒语的关键是,如果你不能改变对象,你将被迫创建一个新对象(具有原始对象的属性加上新对象的属性)。
为了在调度 action 时更新组件,Redux 连接器会检查对象是否不同,而不是属性是否已更改(这要快得多),因此:
我对 Redux(和 React.js)也很陌生,但这是我从学习这些东西中了解到的。
选择不可变状态而不是可变状态有多种原因。首先,突变跟踪非常困难。例如,当你在多段代码中使用一个变量,并且每个地方都可以修改变量时,你需要处理每一个变化并同步变异的结果。在许多情况下,这种方法会导致双向数据流。数据块在函数、变量等之间上下流动。代码开始if-else
被负责处理状态更改的结构所污染。当您添加一些异步调用时,您的状态更改可能更难跟踪。当然我们可以订阅数据事件(例如Object.observe
),但它可能导致应用程序的某些部分丢失更改与程序的其他部分不同步的情况。
不可变状态帮助您实现单向数据流,帮助您处理所有更改。首先,数据从上到下流动。这意味着应用于主模型的所有更改都会推送到较低的组件。您始终可以确保应用程序的所有位置的状态都相同,因为它只能从代码中的一个位置 - 减速器进行更改。还有一件事值得一提——您可以在多个组件中重用数据。状态不能改变(可以创建一个新的),所以在几个地方使用相同的数据是非常安全的。
您可以在此处找到有关可变性优缺点的更多信息(以及选择它作为 Redux 的主要方法的原因):
Redux 通过比较两个对象的内存位置来检查旧对象是否与新对象相同。如果你在 reducer 中改变旧对象的属性,“新状态”和“旧状态”都将指向同一个对象,Redux 会推断出没有任何改变。
没有理由。没有任何根本原因使 shouldComponentUpdate “纯渲染”优化无法与可变状态容器一起使用。例如,这个库就是这样做的。
https://github.com/Volicon/NestedReact
对于不可变数据,对数据结构本身的引用可以用作版本标记。因此,比较您正在比较版本的参考。
对于可变数据,您需要引入(并比较)单独的版本令牌,这很难手动完成,但可以通过智能“可观察”对象轻松实现。