为什么 Redux 中的对象应该是不可变的?

IT技术 javascript angular reactjs redux immutability
2021-03-26 13:06:18

为什么 Redux 中的对象应该是不可变的?我知道某些框架(例如 Angular2)将使用 onPush 并且可以利用不变性来比较视图状态以加快渲染速度,但我想知道是否还有其他原因,因为 Redux 与框架无关,但它在自己的文档中提到要使用不变性(与框架无关)。

感谢任何反馈。

4个回答

Redux 是一个将状态表示为(不可变)对象的小型库。新的状态通过使当前状态通过纯函数来创建一个全新的对象/应用程序状态。

如果你的眼睛盯着那边,别担心。总而言之,Redux 不会通过修改对象来表示应用程序状态的变化(就像使用面向对象的范例一样)。而是将状态更改表示为输入对象和输出对象 ( )之间差异var output = reducer(input)如果你改变了input或者output你使状态无效。

换句话说,不变性是 Redux 的要求,因为 Redux 将您的应用程序状态表示为“冻结对象快照”。使用这些离散快照,您可以保存您的状态或反转状态,并且通常对所有状态更改都有更多的“说明”。

您的应用程序的状态由一类称为 reducer 的纯函数更改。Reducers 有两个重要的属性:

  1. 他们从不变异,返回新构建的对象:这允许在没有副作用的情况下对输入 + 输出进行推理
  2. 它们的签名总是 function name(state, action) {},所以很容易组合它们:

假设状态如下所示:

    var theState = {
      _2ndLevel: {
        count: 0
      }
    }

我们想增加计数,所以我们制作了这些减速器

const INCR_2ND_LEVEL_COUNT = 'incr2NdLevelCount';

function _2ndlevel (state, action) {
    switch (action.type) {
        case INCR_2ND_LEVEL_COUNT:
            var newState = Objectd.assign({}, state);
            newState.count++
            return newState;
        }
    }

function topLevel (state, action) {
    switch (action.type) {
        case INCR_2ND_LEVEL_COUNT:
            return Object.assign(
                {}, 
                {_2ndLevel: _2ndlevel(state._2ndlevel, action)}
            );
    }
}

注意Object.assign({}, ...)每个reducer 中创建一个全新的对象的用法

假设我们已经将 Redux 连接到这些 reducer,那么如果我们使用 Redux 的事件系统来触发状态更改......

    dispatch({type: INCR_2ND_LEVEL_COUNT})

...Redux 将调用:

    theNewState = topLevel(theState, action);

注意:action来自dispatch()

现在theNewState是一个全新的对象

注意:您可以使用(或新语言功能来强制执行不变性,或者小心不要改变任何东西:D

为了更深入地了解,我强烈建议您查看Dan Abramov(创作者)的这段视频它应该回答您的任何挥之不去的问题。

啊好吧 - oop:P。嗯,我不明白当您更改名称时如何实现“冻结对象快照”,因为您随后更改了对象。但无论如何,我认为你是在正确的轨道上。对不变性的关注是因为发生在典型 SPA 中计算经济学,适合浅/哑对象比较重建状态。想象一下,它不是您修改的名称,而是渲染表中 1000 行的单个单元格中的名称。某些事情发生变化时重新渲染表比遍历整个状态来找出究竟发生什么变化要快
2021-05-22 13:06:18
首先让我说谢谢你的回答,我+1你。但是,让我解释一下,我非常了解 Redux 的工作原理,我已经在使用它,我的问题是更深入的思考。想一想,如果您更改名称而不是替换拥有该名称的整个对象,您仍然可以实现“冻结对象快照”。唯一的原因(这可能是一个足够好的理由)是为了速度比较。因为在冻结状态时比较新对象与更改的对象可能更快。那将是我能看到的唯一原因...... tx Sean。
2021-05-27 13:06:18
@AshleyCoolman - 所以从上面继续你的数组示例 - 如果我要更改单元格值并返回相同的数组 - 是否不会重新绘制组件,还是库需要更长的时间才能知道发生了什么变化?在后一种情况下,对不变性的要求更多是一种性能建议。
2021-06-03 13:06:18
我不明白这如何回答“为什么状态应该是不可变的”这个问题。是的,它解释了很多如何去做,只是重复了 Redux 规范:“状态应该是不可变的”,但没有对其背后动机的描述/解释。如果应用程序状态有任何理由来描述转换(更改),那么只需在任何纯函数调用之前和之后使用Objectd.assign({}, ...) 即可
2021-06-07 13:06:18
@ErezCohen 如果你改变一个对象的内部,这个对象仍然是一样的——因此 redux 进行的廉价比较不会注意到变化。您需要返回一个新的顶级对象。facebook.github.io/immutable-js是强制执行此操作的一种方法。
2021-06-17 13:06:18

Redux 文档中提到了以下不变性的好处

  • Redux 和 React-Redux 都使用浅层相等性检查。特别是:
    • Redux 的 combineReducers 实用程序会浅层检查由它调用的 reducer 引起的引用更改。
    • React-Redux 的 connect 方法生成组件,这些组件会浅层检查对根状态的引用更改,以及 mapStateToProps 函数的返回值,以查看包装的组件是否确实需要重新渲染。这种浅层检查需要不变性才能正常运行。
  • 不可变数据管理最终使数据处理更安全。
  • 时间旅行调试要求reducer是没有副作用的纯函数,这样你才能正确地在不同状态之间跳转。
@bluenote10 你可以自由地改变你状态的某些部分,不要听信功能狂热者的意见。不变性是神圣的遗物,与 JS 无关。请阅读有关react+mobx或 的更多信息vue.js
2021-05-28 13:06:18
换句话说,忽略推荐和变异state会干扰“应该更新组件”的逻辑吗?有没有可能说这会以什么方式出错,即渲染是否总是/从不/它取决于?
2021-05-29 13:06:18
他们第一次复制所有对象,比他们说的“浅”比较快。xD 哈哈。我开始打嗝。
2021-06-13 13:06:18
您的问题的答案并非微不足道。这取决于您的状态的结构方式、您正在更改的深层属性以及您是否正在使用combineReducers和/或react-redux. 通过更深入地了解combineReducersand ,您绝对可以知道会发生什么react-redux通常,当由于浅层检查而未检测到深度更改时,UI 中的某些组件将不会更新。但底线是不要改变 state这是一件坏事™。
2021-06-16 13:06:18

Redux 使用不变性的主要原因是它不必遍历对象树来检查每个键值的变化。相反,它只会检查对象的引用是否更改,以便在状态更改时更新 DOM。

伟大的文章https://medium.cobeisfresh.com/how-redux-can-make-you-a-better-developer-30a094d5e3ec

与不可变数据一样,纯函数是函数式编程的核心概念之一

如果函数仅处理不可变数据,则它是纯的。但它是不可逆的。有一些纯函数可以改变数据。可以从不纯的函数组创建纯函数。这个概念很古老,并不完整。不要毒害人的大脑。
2021-06-18 13:06:18