0

我们有以下 ngrx 减速器,但在我们的状态(结构:)中添加了一个嵌套对象settings: { a: boolean, b: string },因此决定使用 lodashcloneDeep(obj)而不是...obj扩展运算符来确保我们的状态是不可变的,根据官方文档“创建减速器功能”</a>部分:

扩展运算符只进行浅层复制,不处理深度嵌套的对象。您需要复制对象中的每个级别以确保不变性。有一些库可以处理深度复制,包括lodashimmer

// before:
export function reducer(state: MyState = initialState, action: Actions): MyState {
  switch (action.type) {
    // ...
    case MY_TASK: {
      return {
        ...state,
        prop1: action.payload.prop,
        prop2: initialState.anotherProp
      };
    }
    // ..
  }
}

当我们将其更改为以下内容时,ngrx 继续调用该reducer()方法,有效地阻止(崩溃)浏览器:

// after - causes infinite loop
import * as _ from 'lodash';
// ..

export function reducer(oldState: MyState = initialState, action: Actions): MyState {
  function nextState(stateChanges?: (MyState) => void): MyState {
    const draftState: MyState = _.cloneDeep(oldState);
    if (stateChanges) {
      stateChanges(draftState);
    }
    return draftState;
  }

  switch (action.type) {
    // ...
    case MY_TASK: {
      return nextState(s => {
        s.prop1 = action.payload.prop;
        s.prop2 = initialState.anotherProp;
      });
    }
    // ..
  }
}

stateChange甚至没有触及新嵌套的附加状态属性(即settings对象),只有prop1, prop2我们之前拥有的平面属性 ( )。settings然而,它是 lodash 的一部分,oldState因此传递给 lodash 的cloneDeep().

知道为什么更改返回的状态(即使用_.deepClone(state)而不是扩展...state运算符)会产生这种效果 - 以及如何阻止它?

我们正在使用@ngrx/entity,所以状态正在扩展EntityState<MyState>。我不理解库的实体函数返回一个新状态(或“如果没有进行任何更改,则返回相同的状态”),并且可以将其他属性(如我们的新嵌套settings对象)添加到该状态,但
“[t]这些属性必须是手动更新”——我认为这意味着我必须自己克隆它们。

我们试图克隆整个状态(扩展EntityState<T>)的错误,而不仅仅是附加属性

工作代码和损坏代码之间的唯一更改是在reducer()方法中进行的,所以我认为我们不会意外触发我们之前没有触发的任何新事件,这通常会导致无限循环......

4

1 回答 1

1

_.cloneDeep is the worst case scenario for ngrx because it means that everything has been touched in the store and it triggers all selectors again.

From the described error I can assume that you have a selector that causes dispatch in some case and because the dispatched action caused _.cloneDeep the selector is triggered again that causes dispatch etc...

于 2020-05-20T19:07:04.250 回答