NgRx: dispatch multiple actions in a single effect
Asked Answered
F

6

22

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.

Fermium answered 8/9, 2021 at 8:39 Comment(0)
A
33

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

EDIT2: The initial link is from a now archived repo. The rule is still correct, it simply got moved to the NgRx core. Check here no-multiple-actions-in-effects

Agitato answered 8/9, 2021 at 9:23 Comment(0)
P
14

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.

Putandtake answered 8/9, 2021 at 16:37 Comment(0)
C
3

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

switchMap(result => ([new Action1(result), new Action2(result)])
Centenarian answered 8/9, 2021 at 9:37 Comment(1)
but as @Putandtake 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.Centenarian
B
3

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")
   )  
})
Barrel answered 18/5, 2023 at 13:21 Comment(0)
D
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.

Disused answered 8/9, 2021 at 10:24 Comment(0)
P
0
private _getAllEntreprises = createEffect(() =>
    this.actions$.pipe(
      ofType(EntrepriseActions.getAllEnterprises),
      switchMap(({ query, page }) =>
        this.service
          .getAllEntreprises(page, query)
          .pipe(
            exhaustMap
            ((response) => {
   
              return[
                EntrepriseActions.getCurrentPage({current:response.current_page}),
                EntrepriseActions.getTotalPages({ total: response.total_pages }),
                EntrepriseActions.getAllEnterprisesCompleted({entreprises:response.enterprises}),
                SettingsActions.loading({ loading: false }),
                SettingsActions.successMsg({ message: response.message }),
            ]})
          )
      )
    )
  );
Polestar answered 5/5, 2024 at 8:58 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Metopic

© 2022 - 2025 — McMap. All rights reserved.