按键修改嵌套对象不会触发 NGRX 中的效果

modifying nested object by key do not trigger effect in NGRX

我有一家 NGRX 商店,如下所示:

export interface INavigationSettings {
  gridLayout: {
    [Breakpoints.Small]: GridLayout;
    [Breakpoints.Large]: GridLayout;
  };
  //...
}

我有一个操作将对那些 GridLayout 应用修改

const SET_NAVIGATION_GRID_VISIBILITY = (state: State, action: featureAction.SetNavigationGridVisibility) => {
  state.navigation.gridLayout[action.payload.size].visibility = {
    ...state.navigation.gridLayout[action.payload.size].visibility,
    ...action.payload.visibility
  };
  return state;
};

此更改已正确应用到商店中

问题是,我的选择器selectNavigationGridLayout

export const selectSettingsState: MemoizedSelector<object, State> = createFeatureSelector<State>('settings');

export const gridLayout = (state: State): {
    Small: featureModels.GridLayout;
    Large: featureModels.GridLayout;
} => state.navigation.gridLayout;

export const selectNavigationGridLayout: MemoizedSelector<object, {
    Small: featureModels.GridLayout;
    Large: featureModels.GridLayout;
}> = createSelector(selectSettingsState, gridLayout);

永远不要捕获任何变化,也不要通过应用程序调用变化状态。 之前当我有一个对象 gridLayout 时它工作正常,但是因为我在做移动,我分开了 2 件 =>

  gridLayout: {
    [Breakpoints.Small]: GridLayout;
    [Breakpoints.Large]: GridLayout;
  };

现在再也不会触发了。

我也试过

return {
   ...state
}

编辑:

我改成了这个

const SET_NAVIGATION_GRID_VISIBILITY = (state: State, action: featureAction.SetNavigationGridVisibility) => {
  return {
    ...state,
    navigation: {
      ...state.navigation,
      gridLayout: {
        ...state.navigation.gridLayout,
        [action.payload.size]: {
          ...state.navigation.gridLayout[action.payload.size],
          visibility: {
            ...state.navigation.gridLayout[action.payload.size].visibility,
            ...action.payload.visibility
          }
        }
      }
    }
  };
};

虽然有效,但是很糟糕,有没有更好的方法?

您最后的编辑有效,因为您return正在创建新状态,而不是改变现有状态。

这里有一些"prettier"解决return新状态的方法。

方案一:ActionReducerMap

另一种解决方案是使用 ActionReducerMap 将你的 reducer 分解为专注于特定的状态。

我看到您的顶级功能名为 settings。所以您的商店看起来有点像这样:

interface StoreState {
  settings: SettingsFeatureState;
}

interface SettingsFeatureState {
  navigation: INavigationSettings;
}

interface INavigationSettings {
  gridLayout: GridLayoutState;
}

interface GridLayoutState {
  [Breakpoints.Small]: GridLayout;
  [Breakpoints.Large]: GridLayout;
}

并且您的设置减速器看起来像以下两者之一:

function settingsReducer(state: SettingsFeatureState, action: Action): SettingsFeatureState {
 // ...
}

// or

function navigationReducer(state: INavigationSettings, action: Action): INavigationSettings {
 // ...
}

const settingsReducer: ActionReducerMap<SettingsFeatureState> = {
  navigation: navigationReducer
};

执行以下步骤进一步分解状态缩减器。

像这样创建一个网格布局缩减器:

function gridLayoutReducer(state: GridLayoutState, action: Action): GridLayoutState {
  // ...
}

const SET_NAVIGATION_GRID_VISIBILITY = (state: GridLayoutState, action: featureAction.SetNavigationGridVisibility): GridLayoutState => {
  return {
    ...state,
    [action.payload.size]: {
      ...state[action.payload.size],
      visibility: {
        ...state[action.payload.size].visibility,
        ...action.payload.visibility
      }
    }
  };
};

然后,修改你的navigationReducer注册gridLayoutReducer如下:

const navigationReducerMap: ActionReducerMap<INavigationSettings> = {
  gridLayout: gridLayoutReducer
}

// This function has the following signature:
// navigationReducer(state: INavigationSettings, action: Action): INavigationSettings 
const navigationReducer = combineReducers(navigationReducerMap)

解决方案 2:克隆状态

如果您真的不想 return 新状态,您可以通过使用 lodash 之类的东西对状态进行细微修改来保留原始逻辑:

const SET_NAVIGATION_GRID_VISIBILITY = (state: State, action: featureAction.SetNavigationGridVisibility) => {
  const newState = _.deepClone(state)

  newState.navigation.gridLayout[action.payload.size].visibility = {
    ...newState.navigation.gridLayout[action.payload.size].visibility,
    ...action.payload.visibility
  };
  return newState;
};

这个 return 的新状态,因为您已经完全克隆了状态。但是,这种方法将使用更多资源,因为您克隆整个状态只是为了修改一些属性。