在NGRX中分派任何动作后执行效果

时间:2019-09-09 08:00:58

标签: angular ngrx ngrx-store

我正在使用NGRX和Angular 7。

我有一家仅用于用户设置(用户首选项)的商店

这是一个简短的版本=>

import { Action } from '@ngrx/store';
import * as featureModels from './models';

export enum ActionTypes {
  SetSettings = '[SETTINGS] Set Settings',
  SetNavigationSettings = '[SETTINGS] Set Navigation Settings',
}

export class SetNavigationSettings implements Action {
  public readonly type = ActionTypes.SetNavigationSettings;
  constructor(public payload: {settings: Partial<featureModels.INavigationSettings>}) {}
}

export class SetSettings implements Action {
  public readonly type = ActionTypes.SetSettings;
  constructor(public payload: {settings: featureModels.ISettings}) {}
}

export type Actions = SetNavigationSettings |
                      SetSettings;

任何设置更改后,我想执行一个效果,它将当前设置存储在本地存储中:

目前,我只是使用这样的选择器,该选择器会在任何状态更改后触发(因此它可以正常工作)=>

export class SettingsEffects {

  constructor(
    private actions$: Actions,
    private dataService: SettingsDataService,
    private localStorageService: LocalStorageService,
    private store$: Store<IAppState>
  ) {
    this.store$.pipe(select(featureSelector.selectSettingsState)).subscribe((settings) => {
          //save
    });
  }

  @Effect()
  init$ = this.actions$.pipe(
  ofType(ROOT_EFFECTS_INIT),
  switchMap(action => {
    const settings = this.localStorageService.retrieve('settings');
    console.log(settings)
    if (settings) {
      return of(new featureActions.SetSettings({settings}));
    } else {
      return EMPTY;
    }
  })
);

但是,这将在初始化时执行,因此在我的INIT效果之前,它将始终使用存储初始状态覆盖localStorage值。这将使我的Init效果仅从本地存储中检索初始状态。

我可以将商店选择放到Init Effect内(并且效果很好)

但是我想知道是否有一种方法不使用选择器/订阅,而只是使用效果。这样,每次用户触发操作时,它就会保存。

1 个答案:

答案 0 :(得分:1)

official NgRx docs中所述,您可以考虑使用MetaReducers。

  

开发人员可以将meta-reducers看作是action-> reduceer管道的钩子。元归约器使开发人员可以在调用常规归约器之前对操作进行预处理。

使用meta-reducer,您可以在每次执行操作时执行代码。 如前所述,该代码在调用普通的reducer之前执行。对于您的情况,要存储新状态(在执行当前操作之后),应在调用reducer(state, action)之后使用返回的状态。

export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
  return function (state, action) {
    const nextState = reducer(state, action);

    console.log('log action', action);
    console.log('log state', state);
    console.log('log next state', nextState);

    // store nextState in local storage
    localStorage.setItem('state', JSON.stringify(nextState));

    return nextState;
  };
}

我已经准备好Stackblitz demo来说明这个答案。

但是,我是否可以通过与您分享个人观点来建议您其他选择?实际上,每个动作都会调用meta-reducer。这可能会导致大量不必要的存储调用。

在这种情况下,我宁愿在每个相关效果中调用另一个特定操作来显式请求状态保存。 但是很明显,缺点是一些重复的代码,并且有错过接听电话的风险。