author avatar
By John McEntee Senior Software Engineer

*Views, thoughts, and opinions expressed in this post belong solely to the author, and not necessarily to SemanticBits.

The purpose of this post is to give developers working on angular + ngrx projects a better understanding of @ngrx/effects. The effects pattern provided by ngrx is easy to implement and follow. However, it does a lot behind the scenes in order to provide the excellent API. New developers often don’t understand what’s going on and that can lead to confusion when they need to step outside the very basic side effects. Click here if you’d like to skip the background provided in the first two sections.

Single State Stores and Flux Architectures

Many developers starting a new angular application are likely to be researching their options for state management. With the advent of flux, and its reference implementation redux, a single state store has become one of the trendiest options available.

Both flux and redux were initially created for managing react applications. While redux can easily be used within any application, competing frameworks began building their own implementations and expanding on the original framework. Perhaps the best example of this is @ngrx for angular, which provides a flux implementation using reactive programming paradigms via rxjs.

We won’t be discussing how a single state store works, why you would use one, or even how @ngrx/store works. What we will discuss is the concept of side effects within a flux architecture and how @ngrx/effects helps us handle them in an angular application.

Side Effects

Side effects, within the context of a flux architecture, are essentially the external activities that need to take place in order to update state within your store. The best, and most common, example of this is making an http call to a web service and using the response to update your applications state.

You generally see side effects implemented in the normal action -> dispatcher -> store pattern. For example:

  1. Dispatch LOAD_POSTS action
  2. Send applicable http request to get all posts
  3. If http request succeeded, dispatch LOAD_POSTS_SUCCESS action
  4. If http request failed, dispatch LOAD_POST_FAILURE action
  5. Store is updated based on action payload from #3 or #4

@ngrx/effects

@ngrx/effects is an opinionated way of managing side effects within your @ngrx based application. Some well-known redux libraries for managing side effects are redux-thunkredux-saga, and redux-observable.

Integrating @ngrx/effects into your angular application is a basic process and is well documented in the link above. What is not described so well in the documentation is how @ngrx/effects works behind the abstraction that is provided via observables and typescript decorators.

Actions

The foundation of effects is the Actions observable. This observable emits all actions that are dispatched to the store. For a more in-depth understanding of how the store and dispatcher work, you should read through this introduction.

When an action is dispatched to the store, it will flow through a specific pipeline. The most important step in that pipeline is the reducer, which is responsible for calculating your new application state. If there are any reducers watching for the dispatched action they will be executed to calculate the new state and that same action will then be emitted by your actions$ observable.

@Effect()
login$ = this.actions$.pipe(
  ofType<Login>(AuthActionTypes.Login),
  map(action => action.payload),
  switchMap((auth: Authenticate) =>
    this.authService
      .login(auth)
      .pipe(
        map(user => new LoginSuccess({ user })),
        catchError(error => of(new LoginFailure(error)))
      )
  )
);

As you can see in the above snippet, our effects return a new observable which has another Action as it’s value. Each of your effects are responsible for emitting a new action that will flow through the same pipeline defined above (this requirement can be specifically ignored for special cases).

 

It is important to be aware of the behavior of the observable returned by the switchMap function. If this observable emits more then one value, you will end up with multiple LoginSuccess actions being emitted back through the pipeline. In some cases (for example, when selecting state from the store) it may be appropriate to use a take(1) to ensure the observable completes.

@Effect() Decorator

The @Effect() decorator is an important part of @ngrx/effects and provides a level of abstraction that is very beneficial for consumers of the library. Without this decorator, the process of managing all effects in the application would be cumbersome. With that said, following how the @Effects decorator is used in the library is a bit difficult. I’ll provide links to source code below, but those will just be starting points. I recommend using these to get a basic understanding. But you should clone the repo and insert breakpoints in the example-app where applicable, so that you can better understand the process.

The effect decorator provided by @ngrx/effects is used to identify the observables in an effect class. At runtime this decorator function is executed and adds metadata to each of the effect class constructors.

This metadata is read when an effect is registered with your module, either at boot of the application, or when a feature module is lazy loaded.

The effect class instances are then emitted via the EffectSources observable.

EffectSources is an internal class that acts as an rxjs Subject and emits all actions from your effects to the store.

Each time a new instance is emitted from EffectSources it uses the metadata registered via the decorator to combine all of the observables in that instance into a single observable.

Using the @Effect() decorator basically allows @ngrx/effects to create a single observable for each of your effect classes. Those observables are a part of the EffectSources subject, and anytime they emit, EffectSources will emit and send the Action to your store.

Conclusion

Hopefully, this article gave you some behind-the-scenes knowledge of @ngrx/effects and pointed you to places so you can dive even deeper. @ngrx/effects is an excellent library that provides robust abstractions and integration with your store. If you understand how and why it does what it does, you can leverage it to the fullest.