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.
Effects, Causes?
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 api
and 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.
The 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 PUT
, POST
, and DELETE
requests, except for the callback property which is used here.
Next Steps
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!
Social