ngrx 中的减速器和不变性

and*_*hil 6 ngrx angular ngrx-store

在 ngrx 中创建 reducer 函数时,我读到的所有地方都说我应该返回原始/先前状态的副本。通过使用扩展运算符或使用库或像JSON.parse(JSON.stringify(state)).

但我在那里发现了一个问题,我找不到任何人在谈论它。reducer 返回的最后一个状态是将与所有当前订阅者和未来订阅者共享的状态。这意味着使用某个存储的所有组件将看到相同的状态对象。

这也意味着如果状态中的任何值在一个组件中发生更改(不分派操作),存储实际上将修改该值,但不会通知其他组件。如果要在任何地方共享,返回当前状态的副本有什么意义?

immutable 这个词一直在使用,但那个状态根本不是不可变的,因为 store 返回它自己的内部对象,而不是它的副本。

我了解不可变部分是否是开发人员需要遵循的概念。但是,原始对象/值的副本需要在使用它的组件中完成。从减速器返回浅拷贝或深拷贝似乎只是浪费处理能力和内存。

dan*_*y74 7

我会试着回答。

伪代码中的减速器如下所示:

myReducer(state, action) {

  switch(action) {

    case ACTION_1:
      return {...state, prop: action.payload}

    case ACTION_2:
      const newState = _.cloneDeep(state)
      newState.prop = action.payload
      return newState

    default:
      return state
  }
}
Run Code Online (Sandbox Code Playgroud)

如果 ACTION_1 您没有改变状态。展开运算符创建一个具有新引用的新对象,而新引用是发出更改信号所必需的。

如果 ACTION_2 您正在克隆状态。你改变克隆状态并返回它。因为克隆状态有一个新的对象引用,它表示已经进行了更改并且每个人都很高兴。

在默认情况下,忽略任何其他操作(例如 ACTION_3)并返回原始状态,表示该状态未更改。对象引用没有改变,因此发出“没有改变”的信号(这就是为什么不要改变原始状态很重要)。

当一个动作被触发时,动作被传递给每个减速器。因此,不想修改其关联状态的 reducer 可以通过依赖于 default case 语句来忽略该操作。

单个操作可以并且经常会触发多个减速器中的状态更改。

如果返回的对象引用发生了变化,它将触发有关特定状态的任何相关 RxJS 状态订阅。使用一些好的 ngrx 选择器可以最小化触发哪些订阅。

PS 有一个名为 ngrx-store-freeze 的很棒的库,它将强制执行“无突变”原则。如果你改变状态,它会提前抛出一个错误。这有助于避免难以追踪的错误。使用元减速器挂钩存储冻结。

PPS 使用对象引用来确定更改的全部目的是因为检查对象引用比检查对象上的每个值以查看它是否已更改要快得多。这就是不变性如此重要的原因。

  • 最大的问题是何时使用扩展运算符以及何时使用 cloneDeep。正如我们所知,扩展运算符是一个浅拷贝。它将包括对原始状态的现有内部对象/数组的引用。因此,如果某个组件依赖于作为状态属性的数组/对象,则不会通知它更改。有时这是需要的行为,有时不是...... (2认同)