【问题标题】:In redux, why do we have to keep the state immutable in reducers?在 redux 中,为什么我们必须在 reducer 中保持状态不可变?
【发布时间】:2017-05-18 03:43:26
【问题描述】:

我想知道为什么我们应该在 reducer 中保持状态不可变。这是强制性的还是仅仅是推荐的方法? redux 是否利用不变性来做一些优化?

经过一些实验,我发现 redux 确实利用了这种不可变性的东西。一个证据是,当我们从 reducer 返回对状态的相同引用时,即使状态内的数据已更改,UI 也不会按应有的方式更新。 如下例所示:

const reducer = (state = [], action) => {
	switch(action.type) {
		case 'BRING_UP_NEXT_IMAGE':
				state.push(state.shift());
				break;
		default: 
			return state;
	}
	return state;
};

UI 没有更新,因为 Redux 检测到相同的状态引用(值得考虑)。

这解释了如果我们想要更新 UI,我们必须返回对状态的新引用的必要性。 但是为什么 Redux 要求内部状态结构也应该是不可变的呢?据我所知,UI 是否更新仅取决于状态引用本身是否更改。即使我改变了内部结构,应用程序也能正常工作,如下所示:

const reducer = (state = [], action) => {
	switch(action.type) {
		case 'BRING_UP_NEXT_IMAGE':
				state = state.concat(state.shift());
				break;
		default: 
			return state;
	}
	return state;
};

是的,我确实使用“state.shift()”改变了之前的状态,但是由于 state.concat 返回了对数组的新引用,所以状态本身是不同的引用,并且应用程序正常工作。

简而言之,我想知道的是,状态是否应该严格保持不可变是使 Redux 正常运行的必要条件,还是推荐的选项,以便应用程序可以通过在 shouldComponentUpdate 中启用浅比较来获得更好的性能绕过对账流程?

【问题讨论】:

    标签: reactjs redux frontend immutability


    【解决方案1】:

    我在我的博文Idiomatic Redux: The Tao of Redux, Part 1 - Implementation and Intent 中深入讨论了这个问题。引用摘要部分:

    核心 Redux createStore 函数本身对您必须如何编写代码只有两个限制:操作必须是普通对象,并且它们必须包含已定义的 type 字段。它不关心不可变性、可序列化或副作用,也不关心type 字段的实际值是什么。

    也就是说,围绕该核心的常用部分,包括 Redux DevTools、React-Redux、React 和 Reselect,确实依赖于正确使用不变性、可序列化的操作/状态, 和纯 reducer 函数。如果忽略这些期望,主应用程序逻辑可能会正常工作,但时间旅行调试和组件重新渲染很可能会中断。这些也会影响任何其他与持久性相关的用例。

    还需要注意的是,Redux 不会以任何方式强制执行不可变性、可序列化和纯函数。 reducer 函数完全有可能改变其状态或触发 AJAX 调用。应用程序的任何其他部分完全可以调用getState() 并直接修改状态树的内容。完全可以将承诺、函数、符号、类实例或其他不可序列化的值放入操作或状态树中。您不应该做任何这些事情,但这是可能

    Redux 文档中Immutable Data FAQ section 中还讨论了 Redux 如何以及为何依赖不变性的详细信息。

    【讨论】:

      【解决方案2】:

      如果您假设当组件在他们订阅的商店更改上重新呈现时“redux 正常运行”,那么只有在您返回状态的新副本时它才能正常运行。

      redux 的 connect 方法已经实现了一个默认的 shouldComponentUpdate,它确实包含了一个身份检查。

      如果您直接修改状态而不创建新身份,您不仅会修改新状态,还会修改旧状态,因为两者都指向存储上的相同引用。具有相同的引用将导致 this.props.somePropFromStore === this.nextProps.somePropFromStore 始终为真。这将导致 redux 不再检测状态更改,除非您实现自定义行为。

      所以我会说它不仅仅是一个“推荐选项”,因为如果你绕过 redux 的基本原则“从不改变数据”,你可以用一个全局 store 类变量替换 redux 存储引擎,该变量保留所有信息。

      redux 文档中也有一段解释它:http://redux.js.org/docs/faq/ReactRedux.html#why-isnt-my-component-re-rendering-or-my-mapstatetoprops-running

      【讨论】:

        【解决方案3】:

        在反应生命周期方法componentWillReceivePropsshouldComponentUpdatecomponentWillUpdate 如果你改变状态 nextProps 和 this.props 将是相同的。它会产生难以调试的问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-02-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-01-14
          • 2018-02-07
          • 2023-03-23
          • 1970-01-01
          相关资源
          最近更新 更多