изменение вложенного объекта по ключу не вызывает эффекта в NGRX - PullRequest
1 голос
/ 22 апреля 2020

У меня есть магазин 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;
};

Это изменение правильно применяется в магазине enter image description here

Проблема в том, мой селектор 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
          }
        }
      }
    }
  };
};

и это работает, но это ужасно, разве нет лучшего способа?

1 Ответ

1 голос
/ 22 апреля 2020

Ваше последнее редактирование работает, потому что вы возвращаете новое состояние, а не изменяете существующее состояние.

Вот несколько «более симпатичных» решений для возврата нового состояния.

Решение 1: ActionReducerMap

Другое решение - использовать ActionReducerMap для разложения ваших редукторов, чтобы они были сфокусированы на определенном фрагменте состояния.

Я вижу, что ваша функция верхнего уровня называется 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
      }
    }
  };
};

Затем измените ваш Navigation-Reducer, зарегистрируйте gridLayoutReducer следующим образом:

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

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

Решение 2: Состояние клонирования

Если вы действительно не хотите возвращать новое состояние, вы можете сохранить исходный лог c с небольшим изменением, используя что-то вроде 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;
};

Возвращает новое состояние, так как вы полностью клонировали это состояние. Однако при таком подходе будет использоваться гораздо больше ресурсов, поскольку вы клонируете все состояние просто для изменения нескольких свойств.

...