Yep, another blog post about Angular 2 and Ngrx. In this post we'll look at some ways to create @Effects that don't return an action to the reducer and when you might want to do this.
Let's take a look at a relatively simple @Effect:
In the code above I'm creating an @Effect named update$. We're listening for actions being dispatched from other places in our app. Normally we won't handle the "START_TIMER" type action in our reducer. It will just "pass through" the reducer and be handled here, as an @Effect. If the action coming in has a type of "START_TIMER" then this effect will go to work! All it's doing though is setting a timer delay for 2 seconds. Then when it's finished it returns an observable to an action of type "TIMER_DONE". Remember, Ngrx takes care of firing off this new action in the background so all you have to do is return an observable with an object that contains the properties of an ngrx Action (ie. type and optionally payload). You can then handle the "TIMER_DONE" type action in your reducer. This normally makes for a great separation of concerns; the effects only have to care about calling out for data and the reducers only need to care about updating the state. @Effects are really for doing any kind of asynchronous action, anything that is a "side effect" (hence the name "ngrx/effects" and name for the @Effect decorator). However, sometimes you might just want to trigger some side effect without really needing to send a response action back to your reducer, and that's what this post is about. :)
I'm using "anonymous authentication" with Firebase to sign in a user automatically as a guest upon landing on the page. In my component I'm listening for the onAuthStateChanged event on the firebase auth object and handling it as a promise:
Let's let's think about these two cases. If the user lands on the page and firebase already has the auth information saved in the session (which is usually the case for returning users) then it will run the code inside the "if" block which dispatches a new action, "ANONYMOUS_AUTH_SUCCESS". We'll pass along the user's name and isAnonymous property so that they can be saved in our ngrx/store (note: we could also use the ngrx-store-localstorage library to save our store after the session ends (ie. if the user closes the browse, opens it back up, and goes back to the page). Anyway, if our user is landing on the page for the first time or has totally cleared their firebase browser history then the else block will run, triggering an action of type "BEGIN_ANONYMOUS_AUTH" to be dispatched. Since this action requires an asynchronous call to firebase to authenticate, the action bypasses our reducer and is handled as an effect. Here's how we might implement it using our standard pattern above:
The problem with returning the "ANONYMOUS_AUTH_SUCCESS" action here is that the firebase call itself will come back and trigger the initial onAuthStateChanged event to be fired again in our component once the user is successfully authenticated. Thus, if we return the "ANONYMOUS_AUTH_SUCCESS" in our effect then it will lead to the action being dispatched twice (which is not the end of the world, but it could make the code more confusing and harder to maintain. It's like an unnoticable bug, and it's hard to spot unnoticable bugs!). It would be much cleaner if we could just call to firebase and then chop off the effect right there in the succes handler. Let's see some ways of doing that.
The thing to remember is that our function has to return an Observable; it can't just return nothing. One simple solution would be to return an action that is dedicated to being the "unhandled" action. In this example below I'm returning an Observable to an action with no payload and with a type, "NO_ACTION":
In the @Effect decorator function we can also pass in an argument to tell the effect, "Hey! Don't actually dispatch the action observable that I'm returning here!". Remember, you still have to return some observable. It could be any Observable, an Observable of an empty object, or even just null.
Remember that most of your @Effects should follow the classic "get an action in, do some async stuff, return an action back with some new data" pattern, and these should be the bulk of your effects. However, for special situations like the one described here it's nice to have the flexibility to create @Effects for doing side effects but then don't return an action. It's great that ngrx/effects allows us to do this, and hopefully next time you want to do it you won't be wondering how! ;)
The posts on this site are written and maintained by Jim Lynch. About Jim...