15

I need to dispatch multiple actions after calling an API request in my effect. I'm using this code at the moment to dispatch one action after API request done:

 changeStatus$ = createEffect(() => 
  this.actions$.pipe(
    ofType(fromJtDetail.changeStatus),
    switchMap(action =>
      this.jtDetailService.changeStatus(action.entity,action.jobTicketId).pipe(
        map(res => fromJtDetail.statusChanged({changedStatus: action.entity.newStatus})),
        catchError(error => EMPTY)
))));

It's important to dispatch more actions in this effect, can't write another effect for this.

Tomas Vancoillie
  • 3,463
  • 2
  • 29
  • 45
Rashed Bayani
  • 161
  • 1
  • 1
  • 4

5 Answers5

22

You can dispatch multiple actions using switchMap + of(

changeStatus$ = createEffect(() => 
  this.actions$.pipe(
    ofType(fromJtDetail.changeStatus),
    switchMap(action =>
      this.jtDetailService.changeStatus(action.entity,action.jobTicketId).pipe(
      switchMap(res => of(
        fromJtDetail.statusChanged({changedStatus: action.entity.newStatus}),
        fromHere.doSmthAction(),      // <-- additional action 1
        fromThere.doSmthElseAction(), // <-- additional action 2
      )),
      catchError(error => EMPTY)
))));

EDIT:
Althought it can be done you should not do it.
Have a look at no-multiple-actions-in-effects

Deitsch
  • 1,610
  • 14
  • 28
12

All of the answers here are correct, the "simple" answer and solution is to return an array of actions. However, this is a bad-practice, for more info see No Multiple Actions In Effects from the docs of NgRx ESLint.

timdeschryver
  • 14,415
  • 1
  • 19
  • 32
2

You can pass the array of actions into a switchMap like below:

switchMap(result => ([new Action1(result), new Action2(result)])
Deitsch
  • 1,610
  • 14
  • 28
Paul
  • 460
  • 2
  • 7
  • 2
    but as @timdeschryver refrences,returning multiple actions in a single effect is typically an anti-pattern. Instead you can create several effects that listen to the same action. – Paul Mar 17 '22 at 19:58
1

Of course other answer mentioned fairly that you can use the injected Store reference or switchMap to dispatch multiple actions, but it is worth noting that this is not considered a particularly good practice, as it can obscure intent in some cases, and make other effects (that are triggered as a result of this one) harder to reason about. You can find a detailed explanation of how to overcome this here, but briefly, an effect should only ever dispatch one and only one action, and then other effects (or reducer handlers) should additionally listen to that one action too. The link I provided also has examples of how to change to code to a more correct version.

Armen Vardanyan
  • 3,214
  • 1
  • 13
  • 34
0

This Answer might help for other people seeking to solve multiple actions in effects.

As other people already answered,

  1. You can dispatch multiple actions in one effects (return Array of actions, or use store to dispatch (this.store.dispatch(otherAction()) - BUT NO!

  2. You SHOULD NOT dispatch multiple actions in one effects as it is anti-pattern (https://github.com/timdeschryver/eslint-plugin-ngrx/blob/main/docs/rules/no-multiple-actions-in-effects.md)

SOLUTION : Effects Chaining (One effects triggers another effects)

updateAPILoadingState$ = createEffect(()=>{
 return this.action$.pipe(

 ofType(getAPIAction), // <-- same action which below effects uses to update loading status 

 exhaustMap(()=>{
     return updateLoadingState("LOADING")
          })
 )    
})

getSomeInformationFromAPI$ = createEffect(()=>{
return this.action$.pipe(

 ofType(getAPIAction), // <--- Listens to the Action

 exhaustMap(()=>{
     return this.apiService.getSomething().pipe(
                map((apiResponse)=>
                     postAPISuccess(apiResponse)) // <-- trigger action to save the response
          })
 )  
})

 postAPISuccessEffect$ = createEffect(()=>{
   return this.action$.pipe(

   ofType(postAPISuccess), // <--- EFFECTS CHAIN : listen to the action which was triggered by above effect

   exhaustMap(()=>{
     return updateLoadingState("LOADED")
   )  
})
Rafique Mohammed
  • 3,666
  • 2
  • 38
  • 43