Start using ngrx/effects for this

profile
Tim Deschryver
timdeschryver.dev

You’re probably only using @ngrx/effects to handle the communication to an external source by triggering an Effect with an NgRx action. But did you know that Effects can be used for more than this?

Effects Basic link

The @ngrx/effects package provides a way to isolate side effects into its model, outside the NgRx store and the Angular components.

It has an Observable Actions, which is a stream of all the dispatched actions to the NgRx Global Store. For every dispatched action, the Actions Observable emits a new value (after all of the reducers are invoked).

Because we don't want to trigger all the registered Effects when an action is dispatched, we can use the ofType operator. This acts as a filter, and will only invoke the Effect when it receives the configured action(s).

A typical Effect uses the Actions Observable as its source and uses the ofType operator to only perform its side effect when the corresponding action is dispatched. For example, if we want to retrieve customers from a web service, we need to create an Effect.

The Effect listens to every action that gets dispatched and when it retrieves an action with the action type [Customers Page] Loaded (CustomerPageActions.opened), it will fetch the data over HTTP. Depending on the HTTP response, the Effect either dispatches a [Customers Api] Load Success action (CustomerApiActions.loadCustomersSuccess) if the request was successful or a [Customers Api] Load Failed action (CustomerApiActions.loadCustomersFailed) if the request has failed.

To retrieve the customers within the application, we have to dispatch the [Customers Page] Get action (CustomerPageActions.opened). Inside the component where we want to show a list of all the customers, we have to use a selector to select all the customers from the Global Store state.

External sources link

Effects are not limited to be triggered by the Actions Observable. While the Actions Observable is the most known and the most used source for an Effect, we can use everything that is an Observables as the source. When that source emits a new value, the Effect is triggered.

Using RxJS Observables link

For example, with the interval function we can dispatch an action every x milliseconds.

Using a selector link

Every time when the selector emits a new value, the Effect is invoked. Then you're using this, be careful that the Effect doesn't result in a state change because you might end up in a loop.

Using the JavaScript API with RxJS link

We can use RxJS to create a reactive JavaScript API that dispatches actions to the NgRx Global Store, and the application.

format_quote

Bonus tip, Jan-Niklas Wortmann created rxjs-web that wraps the web API with Observables.

Reusing Effects link

An Effect can listen to multiple actions. This makes it easy to reuse the logic of an Effect for different actions that have the same behavior.

To listen to more than one action, simply add more actions to the ofType operator.

Handling the flow of a (Angular Material) dialog link

Instead of handling a dialog inside a component, I find it easier to write the flow of the dialog as an Effect. The Effect opens the dialog, and when the dialog closes it returns a corresponding action back to the Global Store.

Showing notifications link

Just like the dialog example, I like to handle my notifications within an Effect. Doing this keeps the rest of the application pure and more understandable in my opinion. In the example below, we use the Angular Material Snackbar but the same can be applied to any other notification system.

For Effects that don't result in another action (like this one), it's important to set the dispatch option to false, otherwise, the incoming action will be re-dispatched resulting in an infinite loop (and in this case a lot of notifications). For more info about non-dispatching Effects, see the docs.

Enhance your action with Global Store state link

This is useful for when you want to enhance an action with data from within the store state. This isn't a silver bullet, it's probably easier (to write/test/read) to add the data to the action when it's dispatched. But when that data isn't available when the action is dispatched, use the NgRx concatLatestFrom operator to retrieve the data with a selector. It's similar to RxJS's withLatestFrom operator with the exception that concatLatestFrom is lazy, meaning that it will only invoke the selector when the effect receives a filtered action.

To take it a step further, the data retrieved by the selector can be used to check if an entity already exists in the store. Depending on the outcome we can abort the Effect early. This gives us the power to block unnecessary GET requests if the entity is already persisted in the store, if not we can fetch the entity.

Prefetch data link

In Making your application feel faster by prefetching data with NgRx we saw how we can prefetch the details of entities with NgRx when an entity is visible or when the user hovers over a link.

With Effects, it's easier to achieve the same result but both serve a different purpose.

By using an Effect, we don't need a user interaction to prefetch the data. Instead, we chain multiple Effects together by using the result of another Effect.

I've used this approach in a calendar to fetch the data for the next week. This makes moving between weeks smoother and ensures that the initial load is fast.

To implement this behavior we need two Effects. The first Effect loads the initial set of data (the current week) when the page is loaded. The second Effect loads a different set of data (the next week) when the initial set is loaded. The second Effect does this by listening to the successful action of the first Effect.

Analytics/monitoring link

Because every dispatched action emits a new value to the actions source, we can use this source to gain statistics of the application. For instance, we could log every dispatched action or only log the actions important. You can filter out actions by using the ofType operator, or another filtering operator, for example, the RxJS filter operator.

Listen to router link

Listen to router changes with the @ngrx/router-store selectors and dispatch an action with the new route data. An alternative would be to injected the ActivatedRouterData into the component and to dispatch an action on init. But this leaves us with duplicated logic to parse the route data.

Change the windows title link

Add data to the route data and update the window title after navigation.

By injecting the Angular router into the Effects class it’s possible to redirect the user based on certain actions. In the example below, we redirect the user to the homepage when the user logs out.

Notice that in this example, we're also using a non-dispatching Effect.

Conclusion link

Knowing this, we can refactor some code that now lives inside our components or inside our NgRx store, into the ngrx/effects model. By doing this, it makes our components more pure and it keeps the side effects of our application separated resulting in code that is easier to reason about and also easier to test.

Now that you know for which cases you could use effects, you should also check out when to not to use effects.

format_quote

Not found what you were looking for? Try a similar post, Angular.Schule → 5 useful NgRx effects that don't rely on actions, written by Ferdinand Malcher

Incoming links

Outgoing links

Feel free to update this blog post on GitHub, thanks in advance!

Join My Newsletter (WIP)

Join my weekly newsletter to receive my latest blog posts and bits, directly in your inbox.

Support me

I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.

Buy Me a Coffee at ko-fi.com PayPal logo

Share this post