Start using ngrx/effects for this
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_quoteBonus 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.
Navigate based on actions link
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_quoteNot 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
- Testing an NgRx project
- Making your application feel faster by prefetching data with NgRx
- An experiment, using the global NgRx Store as a local store
- Managing different slices of the same NgRx state
- Common and easy-to-make mistakes when you’re new to NgRx
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.