Rashed Bayani
Rashed Bayani

Reputation: 251

NgRx: dispatch multiple actions in a single effect

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.

Upvotes: 24

Views: 43167

Answers (6)

Deitsch
Deitsch

Reputation: 2178

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

Upvotes: 35

oussama mazeghrane
oussama mazeghrane

Reputation: 1

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 }),
            ]})
          )
      )
    )
  );

Upvotes: 0

Rafique Mohammed
Rafique Mohammed

Reputation: 3796

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")
   )  
})

Upvotes: 5

timdeschryver
timdeschryver

Reputation: 15497

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.

Upvotes: 16

Paul
Paul

Reputation: 478

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

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

Upvotes: 3

Armen Vardanyan
Armen Vardanyan

Reputation: 3315

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.

Upvotes: 1

Related Questions