Monday, September 04, 2017

NGRX Store and State Management 3

In previous articles I described the basic Store and Reducer layout of the pattern.

Reducers are the only way state is modified, and the modifications should be pure, meaning synchronous functions. Anything asynchronous should be handled elsewhere. Other desired side effects may be chaining of actions, where one action sets in motion a chain of changes and asynchronous calls.

The NGRX solution is Effects. These are essentially a subscription to the Action observable. Every action can be looked at, some side effect applied, and a new action mapped into the stream to be dispatched.

Here are the actions.

export const ARTICLES_LOAD = 'Load Articles';
export const ARTICLES = 'Article list';

export class ArticlesLoadAction: Action {
   type = ARTICLES_LOAD;
}

export class ArticlesAction: Action {
   type = ARTICLES;
   constructor(public payload: Articles[]) {}

export type Action 
   = ArticlesLoadAction
   | ArticlesAction;

The ARTICLES type action was handled in the articlesreducer, inserting the array of articles into the state. But the ARTICLES_LOAD action was not. It is a command action, and here is where Effects do their magic.

@Injectable()
export class ArticlesEffects {
   constructor(private actions$: Actions,
                      private http: Http
) {}

@Effect() loadarticles$ = this.actions$
   .ofType(ARTICLES_LOAD)
   .mergeMap(() => this.http.get(api)
      .map(response => response.json())
      .map(articlearray => new ArticlesAction(articlearray))
      .catch(err => Observable.of(new ErrorAction(err))
   );

Effects are an injectable service. Actions, the action observable stream is injected in the constructor, along with any services required. 

The effect is an observable chain, starting with ofType, which is similar to .filter. We are only interested in ARTICLES_LOAD. Multiple actions can be listed, 

.ofType(ARTICLES_LOAD, ARTICLES_RELOAD)

mergeMap calls a function that returns an observable, merging the results back into the actions$ stream. The response is mapped to an action with the array of articles as payload. The reducer is listening for this action, and the array will be inserted into the state.

Note the .catch is off the http.get observable? Effects in v2 of NGRX would complete if the .catch was off the actions$ observable. As well, if you .map the values off the http.get you can return a type that the actions$ observable likes.

If you want to dispatch multiple actions, replace the .map with
.mergeMap(data => [new Action1(data), new Action2(data)])

and each action will be merged into the stream for dispatch.

In some situations you may not want to dispatch a new action. 

@Effect({dispatch: false}) = ...

The possibilities are almost endless, and this is where much of the work of getting and posting data will occur.

To initialize the effects,

EffectsModule.forRoot([ArticlesEffects]),

in the import: [] section of the app.module.ts. It takes an array of injectable effect classes.



Comments: Post a Comment

Subscribe to Post Comments [Atom]





<< Home

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]