ngrx 8: dispatch multiple actions in single effect

Viewed 6304

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.

5 Answers

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.

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}),
        fromThere.doSmthAction(),    // <-- additional action 1
        formHere.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

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

switchMap(result => ([new Action1(result), new Action2(result)])

You can inject Store into your class, and use it to dispatch as many actions as you want, like the following:

import { Store } from '@ngrx/store';
....

// >>>> AppState is your state model
constructor(private store: Store<AppState>) {}

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 })),
                // >>>>> dispatch a new action using this.store.dispatch
                tap((res) => this.store.dispatch(new ACTION_TO_BE_DISPATCHED())),
                catchError((error) => EMPTY)
            )
        )
    )
);

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.

Related