28

I have an Angular 8 app which uses NgRx 8.3.0

For one of the actions, I need the reducers to execute before the effects, because the effect depends on the reduced state. Does NgRx guarantee that order, or is there a way to force that order?

James Taylor
  • 413
  • 4
  • 6
  • Sounds like it's guaranteed: https://github.com/ngrx/platform/issues/162 – kekdahl Jan 31 '20 at 01:57
  • Per the accepted answer, Brandon Roberts who answered the question in the above github link is part of NgRx core team. Many of his talks are on youtube. – James Taylor Jan 31 '20 at 20:11

2 Answers2

40

Edit:

NgRx effects fire after all reducers for that action have executed. That order is guaranteed. The reduced state is the payload of your effects.

Found the following comment in lifecycle_hooks.d.ts in the NgRx effects-build project:

By default, effects are merged and subscribed to the store. Implement the OnRunEffects interface to control the lifecycle of the resolved effects.

https://github.com/ngrx/effects-builds/blob/master/src/lifecycle_hooks.d.ts

Effects subscribe to the store and they fire when the state store changes. Because we know changes to the state are handled by the reducer, we can reasonably conclude that reducers will always run before the effects.

Also, found an answer from Brandon Roberts, a Google Developer Expert and a member of the NgRx core team, confirming that the order is guaranteed. https://github.com/ngrx/platform/issues/162

Kyler Johnson
  • 1,939
  • 16
  • 14
  • @JamesTaylor you're correct. Good call! I just found where Brandon Roberts, a NgRx core team member, said that it was guaranteed. From looking at the NgRx source code, it looks like all reducers for an action execute before any effects for that action and that order is guaranteed. I've updated my answer accordingly. – Kyler Johnson Jan 31 '20 at 16:35
  • Great, thanks for the followup!! Do you have any links you can post regarding the source code or Brandon's blog/video? I'm just getting started with NgRX so still trying to find my way through it. – James Taylor Jan 31 '20 at 17:24
  • I do and updated my answer with them so that anyone else searching for answer to this doesn't have to read our dialogue to get all of it. – Kyler Johnson Jan 31 '20 at 20:00
  • How about other additional subscribers to the action stream, e.g. in component code via the `ofType` operator? Are they guaranteed to execute after effects (and hence after reducers according to your answer)? – lcn Feb 17 '21 at 07:10
-2

I have a counterexample to the top answer. I have an action called UpdateToken with this effect:

      @Effect({dispatch:false})
  updateToken$ = this.actions$.pipe(
    ofType<UpdateToken>(AuthActionTypes.UpdateToken),
    mergeMap((action) => this.auth.getUsernameFromToken(action.token).pipe(map(username => {
      action.username=username;
          })
        )
      ),
    catchError(()=> EMPTY)
  );

and this reducer:

case AuthActionTypes.UpdateToken:
  console.log("update token returning new state...... action is ",action);
  return {username:action.username,
          token:action.token}; 

notice that the effect updates the value of action.username. When I dispatch an action, the updated value of the username is printed by the reducer. That means that when the reducer is running, the effect has already changed its value, e.g. the effect happened first. So, WTF?

Hey Zain, edit this.

  • i suggest, you read the guidelines first for writing a good answer – Zain Ul Abidin Jun 24 '22 at 06:28
  • 1
    @OwenJones what is happening here is probably not the effect running before the reducer, what you are seeing when you console.log an object is a live object - if code updates that object after it is logged, you will see it in the more recent state rather than every property at the exact moment it was logged. This is a expected behaviour - see https://developer.mozilla.org/en-US/docs/Web/API/Console/log#logging_objects Try just console.log(action.username) - as username is a string which is a primitive it will not update after it is logged. – bzzWomp Sep 02 '22 at 07:33