Hongbo Miao Hongbo Miao - 2 months ago 41
TypeScript Question

How to get access of the state tree in effects? (@ngrx/effects 2.x)

I am updating @ngrx/effects from 1.x to 2.x

In 1.x I have access of state tree in effect:

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 }));


Now in 2.x, it only gives me action. Is there still a way to get access of the state tree? Or should I avoid using like this because it is not a good practice?

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 }));

Answer

Effects don't have to be class properties; they can be methods, too. Which means you can access a store that's injected into the constructor.

With the injected store, you should be able to use an operator like mergeMap to get the state and combine it with the update you received:

@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 }));
  }
}

Whether or not doing this is good practice is a matter of opinion, I guess. Reading the state - ideally by composing an ngrx-style selector - sounds reasonable, but it would be cleaner if all of the information you needed for a particular effect was included in the action to which it was listening.

Comments