In the last article, we set up an automatic state generator function to create an object to hold our data, a reusable higher-order function that created reducers to interact with our data, and a function that returned a tree of selectors so we could access our state. Now, on our journey to automate away a lot of the effort required to set-up a state management system, we are going to write the effects (AKA middleware in Redux) to pass new data to our state. Before we jump right into the code, I'll give a quick overview of an effect.
If you've never seen effects before, they'll look a little strange at first, but you can think of them the same as a reducer. Both effects and reducers take in an action and "do something" based on the type of the action. In fact, when you dispatch an action to the store, the action passes through all reducers and effects simultaneously. For this reason, your action types should be globally unique for effects and reducers otherwise you'll have multiple changes to state. There are cases when you want that to happen, for example on an action dispatch, the reducer can update the status to 'LOADING' and when the effect completes, it can update to 'SUCCESS' or 'FAILURE'. The main difference between reducers and effects is that effects are mainly for asynchronous actions and they generally return another action on completion, and that is accomplished through the use of Observables.
Creating the Effect
Now that the foundation has been set, let's get our hands (or fingers) dirty with some effects that deal with AJAX calls.
At the top, we have our imports which are pretty self-explanatory so I won't go over those. The next thing we have is the interface,
Request, that defines what we can expect our action to contain. The
api property contains all the information we need to complete our asynchronous request,
callback is a function that we want to trigger upon completion of the effect,
path describes how to get to the slice of state using lodash's
get method, and
module specifies which reducer we will send the result on completion. The set-up to deal with some these properties, namely
module was completed in the last article so if you feel lost, go back for a refresher.
Now in to the actual effect. We have to use the
Injectable decorator since effects function like services in Angular so we throw that at the top. Then we inject the observable
Actions to keep track of all actions that are being dispatched and
HttpClient to make our AJAX requests. Finally we get into handling our first action.
Effect decorator lets Angular know that actions should be passed through the function (the actions will be ignored without it). Every action is then piped through the
ofType function which checks the
type property on the action and only allows matching actions through. Then we pass the action to
mergeMap, which takes in a parameter or an Observable and returns a new Observable, to make the
GET request. If it's successful, we map the response to a new action (this one goes to our reducer), with the type based on the
module property and pass the
path information and the response in the
payload property. Finally to cover our bases, we use
catchError to, as the name suggests, catch errors and send back and error action.
The shown case only covers
GET requests, but the pattern is essentially identical for
DELETE requests, except for the callback property which is used here.
If you've managed to understand this far, you've made it through the toughest part *pats self on back* and are on the home stretch! So far, we've completed the wiring for our state, reducers, and effects, we just need to tie everything together through a Component. And that is exactly what I will go through in the next article. See you next time!