1

this stackoverflow: NgRx - Order of execution of Reducers and Effects says that effects run before reducers in ngrx. However my 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)
);

runs before my reducer:

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

which I can tell since the effect updates action.username, and the reducer prints it.

What's more, it doesn't even make sense to me why effects would run before reducers.It doesn't make sense because the effect is where you are meant to make requests according to the docs, and you may need to update the state based on the result of the request, which you couldn't do if the reducer ran first.

Can anyone please help me understand? (Please don't say something like "you're an idiot. you dont understand how any of this works and your question is an error." :))

The Fabio
  • 5,369
  • 1
  • 25
  • 55
  • 1
    can you please create a [stackblitz](https://stackblitz.com/fork/angular-ivy) showing the order of the execution of your action by the reducer and related effect? It will help you understand how it works and make it easier for people to offer you guidance – The Fabio Jun 23 '22 at 04:06
  • I would if I could but the amount of code I’d need to put in the stack blitz to make this actually run is enormous and also for compliance reasons I can’t post parts of that so I apologize but i cant – Owen Jones Jun 24 '22 at 05:04
  • you don't need to place your code there per say, only a Minimum reproducible snippet where the problem happens. I do a lot of it myself – The Fabio Jun 24 '22 at 05:08
  • in order for this line to run: this.auth.getUsernameFromToken(action.token) a very large amount of code needs to be included and you need to be on a certain vpn – Owen Jones Jun 24 '22 at 15:55

1 Answers1

1

The issue you are facing is about understanding the life-cycle of Actions when using effects. The way the store action's work-flow is designed to work with an effect goes usually like this:

  1. Store dispatch Action1
  2. Reducer Processes Action1 and updates the state
  3. Effect Processes Action1 (this effect calls a service)
  • If service call succeeds, the Effect dispatches Action2 containing a Success payload
    • 4.a Reducer Processes Action2 and updates the state
  • if service call errors, the Effect dispatches Action3 as an Error containing the error payload
    • 4.b Reducer Processes Action3 and updates the state

very important: The reduces do not change the action payloads

In your case you are attempting to update the payload of "Action1" from the work-flow above, presumably for the same action to be processed with different payloads along its life-cycle. This is unusual and an incorrect way of using actions with effects...

You should instead change you effect to:

  • dispatch a success action with the payload of the successful service
  • dispatch a failure action when it fails.

You don't need to process the failure action (although it would be good to do so, for UX), but your reducer should process the Success action payload.

To achieve this you need to

  1. Create 2 new actions "UpdateTokenSuccess" and "UpdateTokenFailure"
  2. Change your effect like this (apologies if the syntax to create the action is not right, has been a while since I last did ngrx v7):
@Effect()
updateToken$ = this.actions$.pipe(
 ofType<UpdateToken>(AuthActionTypes.UpdateToken),
 mergeMap((action) =>
   this.auth.getUsernameFromToken(action.token).pipe(
     map((username) => new UpdateTokenSuccess({ username })),
     catchError((error) => of(new UpdateTokenError({ error })))
   )
 )
);
  1. update the reducer to cater for the success action and the request actions in separated (again, apologies for syntax errors):
case AuthActionTypes.UpdateToken:
    console.log("UpdateToken", action);
    return {token:action.token}; 
case AuthActionTypes.UpdateTokenSuccess:
    console.log("UpdateTokenSuccess", action);
    return {username:action.username}; 

Hopefully this sets you for the win

The Fabio
  • 5,369
  • 1
  • 25
  • 55