在 NGRX 中的任何动作调度后执行效果

Execute effect after any action dispatch in NGRX

我正在使用 NGRX 和 Angular7.

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

这是一个简短的版本 =>

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 中(而且效果很好)

但我想知道是否有不使用 selector/subscription 而只使用效果的方法。这样每次用户触发一个动作,它都会保存。

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

Developers can think of meta-reducers as hooks into the action->reducer pipeline. Meta-reducers allow developers to pre-process actions before normal reducers are invoked.

使用 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。这可能会导致大量不需要的存储调用。

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