How to get Store state in ngrx Effect

Viesturs Teivāns
2 min readJan 6, 2017

--

The RxJS withLatestFrom operator.

TL;DR

To get the latest state of other Observable use withLatestFrom operator (source):

@Effect() specialEffect$ = this.actions$
.ofType(SOME_ACTION)
.withLatestFrom(this.store$)
.map(([action: Action, storeState: AppState]) => {
// Do something ...
});

What’s ngrx Store and what are Effects?

Ngrx store is a redux store for Angular 2, just like Redux for React.

Ngrx effects are side-effects that may happen after an action on the store. They’re mostly used for HTTP requests, like logging user in after he executed login action, but can be used for any side-effect you need. In the very essence effect triggers an action in response to another action, and before doing that may perform any kind of logic.

Why do I need Store state in Effect?

It’s useful when the logic you want to perform depends on the store state.

For instance, I have a store of lines and a store of pages. I want to increase the number of pages after every 100 lines.

It could look something like this:

Simple reducers

Now to add the page a line you would dispatch an action something like this: store.dispatch({type: ADD_LINE, payload: 'a new line'});

How to get the Store state in an effect?

First I need to create the effect.

Line effects

The effect is quite simple. When ADD_LINE action is dispatched, I increase the page count. The only problem is, I only want to increase the page count after each 100 lines. To do this I should know how many lines there are.

It actually isn’t that hard to do when I remember, that the effect is actually an Observable and store is an Observable as well. There are multiple ways you can combine Observables. Since I only want to get the Store state once an action happens, withLatestFrom() operator is what I need.

Effect with `withLatestFrom` operator

When using withLatestFrom operator, the next operator receives an array of previous Observable contents and the one from withLatestFrom.

P.S.

There are couple of more useful things to know.

  • When passing an array of multiple Observables to withLatestFrom you’ll get only one value — from the one Observable that produces latest.
@Effect() specialEffect$ = this.actions$
.ofType(ADD_LINE)
.withLatestFrom([
this.store$.select(state => state.lines),
this.store$.select(state => state.pages])
.map(([action: Action, linesOrPages: Lines|Pages]) => {
// Do something ...
});
  • When using withLatestFrom multiple times to combine Observables, you’ll get the values from both Observables in form of an array within an array.
@Effect() specialEffect$ = this.actions$
.ofType(ADD_LINE)
.withLatestFrom(this.store$.select(state => state.lines))
.withLatestFrom(this.store$.select(state => state.pages))
.map(([[action: Action, lines: Lines], pages: Pages]) => {
// Do something ...
});
  • You can alter the format in which the values will be returned with the resultSelector method.
@Effect() specialEffect$ = this.actions$
.ofType(ADD_LINE)
.withLatestFrom(this.store$.select(state => state.lines))
.withLatestFrom(
this.store$.select(state => state.pages),
([action, lines], pages) => {
return new Array<[Action, Lines, Pages]>([action, lines, pages]);
})
.map(([action, lines, pages]) => {
// Do something ...
});

--

--

Viesturs Teivāns

UI/UX enthusiast, Full stack developer, CTO at @froontapp