I asked a question a while ago surrounding the @ngrx/effects
library but the responses do not resolve my issue entirely.
I am now happy that I understand how actions and effects relate to each other, but I am still unsure as to what functions they perform. The example app goes some way to helping me, but it seems that the effects are doing all the grunt work: the loadCollection$
effect, for example, appears to be loading all of the resources; and a subsequent dispatch of an AddBookSuccessAction
appears to add the book to the collection.
In this app, the AddBookAction
appears to do nothing in the reducer, but the effect is loading the resource and dispatching a success action.
I guess really what I want to know is about separation of concerns: what should actions be doing, and what should effects be doing?
When you introduce
@ngrx/effects
into your application, you need to think a little bit differently. Take for example a login component and a user stateWithout using effects, your
login
implementation might look something likeWhen the
dispatch
is called, the action is used by the reducer to update the user state. So the action doesn't really do anything. The reducer can use the payload to update the state, based on the action. The subscribing (in this case)AppComponent
will get the updated user.When you introduce effects, the main difference is how the
login
is handle. Nothing changes about theUSER_UPDATED
action or the user reducer. What changes is that you introduce another action, that is dispatched by thelogin
method. So now your newlogin
is simplyNow the purpose of the effect, is to handle what would normally have been handled in the
login
. It will listen for theLOGIN
action, and then transform itself into theUSER_UPDATED
action.You can see that from the
LOGIN
action, we get the credentials, and useswitchMap
to return different Observable, one that contains the new (UPDATED_USER) Action. So it's just a transformation. No one has to call the effect. This is automatically subscribed to.The benefit of doing it this way, is that it makes your component simpler, and easier to test. Also puts all the side effects into one place, which makes it easier to reason about and test. All your components do is subscribe to state and dispatch actions. The effect will handle all the legwork and dispatch the appropriate resulting actions.
As far as the "Action Classes", these are not mandatory. An
Action
is nothing more than an object with two propertiestype
andpayload
that get passed to the reducer. The purpose of having action classes is to act as action creators. For exampleNow instead of creating the actions yourself, you just call the appropriate method to get the action
You can see the last
map
call where we create theAction
just be calling theuserActions.updateUser
.There are a few benefits to doing this. For one, you get strong typing. When you create the action yourself, there's a chance you might pass the wrong payload. But when you have a method for which you know what the argument type needs to be, you have less chance of a mistake.
Also when you use the action creators, you can accept different arguments. It doesn't have to be the payload. This way you can create the payload yourself (and may introduce some logic) in a reproducible and testable way.
See Also: