Как получить доступ к дереву состояний в эффектах? (@ngrx/effects 2.x)

Я обновляю @ngrx/effects от 1.x до 2.x

В 1.x у меня есть доступ к дереву состояний:

  constructor(private updates$: StateUpdates<AppState>) {}

  @Effect() bar$ = this.updates$
    .whenAction(Actions.FOO)
    .map(obj => obj.state.user.isCool)
    .distinctUntilChanged()
    .filter(x => x)
    .map(() => ({ type: Actions.BAR }));

Теперь в 2.x это только дает мне действие. Есть ли способ получить доступ к дереву состояний? Или мне следует избегать такого использования, потому что это не очень хорошая практика?

  constructor(private actions$: Actions) {}

  @Effect() bar$ = this.actions$
    .ofType(ActionTypes.FOO)
    .map((obj: any) => {
      console.log(obj);              // here is action only
      return obj.state.user.isCool   // so it is wrong here
    })
    .distinctUntilChanged()
    .filter(x => x)
    .map(() => ({ type: ActionTypes.BAR }));

Ответы

Ответ 1

Другой способ - использовать .withLatestFrom(this.store). Таким образом, полный код:

  constructor(
    private actions$: Actions,
    private store: Store<AppState>
  ) {}

 @Effect() bar$ = this.actions$
    .ofType(ActionTypes.FOO)
    .withLatestFrom(this.store, (action, state) => state.user.isCool)
    .distinctUntilChanged()
    .filter(x => x)
    .map(() => ({ type: ActionTypes.BAR }));

Ответ 2

Эффекты не должны быть свойствами класса; они также могут быть методами. Это означает, что вы можете получить доступ к хранилищу, который был введен в конструктор.

Во время написания этого ответа семантика объявлений свойств и параметры конструктора public/private мне не поняли. Если свойства объявлены после конструктора, они могут получить доступ к членам public/private, объявленным через параметры конструктора, - поэтому вам не нужно объявлять свои эффекты как функции.

В загруженном хранилище вы можете использовать оператора типа mergeMap для получения состояния и объединения его с полученным вами обновлением:

@Effect()
bar$(): Observable<Action> {

  return this.actions$
    .ofType(ActionTypes.FOO)
    .mergeMap((update) => this.store.first().map((state) => ({ state, update })))
    .map((both) => {
      // Do whatever it is you need to do with both.update and both.state.
      return both.update;
    })
    .distinctUntilChanged()
    .filter(x => x)
    .map(() => ({ type: ActionTypes.BAR }));
  }
}

Я думаю, что это хорошая практика - это мнение. Чтение состояния - в идеале, составление селектора ngrx-стиля - звучит разумно, но было бы более чистым, если бы вся информация, необходимая для определенного эффекта, была включена в действие, которое оно слушало.