ultimatecourses/ngrx-store-effects-app

Dispatching multiple actions on a single effect?

Closed this issue ยท 17 comments

Hi,
First of all, thank you for the course. It is a great learning resource.

I am trying to dispatch multiple actions on a single effect. I don't believe you cover that in your course. I was wondering if a code snippet, perhaps maybe in context of the Pizza app would help others (including me) in the future?

Thank you

@cyberbeast use switchmap and pass it an array of actions you want to dispatch.

@Effect()
loadPizzas$ = this.actions$.ofType(pizzaActions.LOAD_PIZZAS).pipe(
  switchMap(() => {
    return this.pizzaService
      .getPizzas()
      .pipe(
        switchMap(pizzas => [
          new pizzaActions.LoadPizzasSuccess(pizzas),
          new pizzaActions.AnotherAction(pizzas)
        ]),
        catchError(error => of(new pizzaActions.LoadPizzasFail(error)))
      );
  })
);

Hope that helps.

Hey @Ihatetomatoes, would this work for actions from different stores? If not, how would one do that?
F.eks. to trigger notification action (and its effects) from another store.

(i tried similar thing a bit while ago, but last thing I remember from is that the actions would indeed be dispatched, but essentially from a different store, so the effects associated with those actions would not trigger)

@dakipro I can attest: yes, this does work across stores. Here's an example I've adapted from some source I was working on:

@Effect()
save$: Observable<Action> = this.actions$.pipe(
    ofType<documentActions.Save>(documentActions.SAVE),
    mergeMap((action: documentActions.Save) => {
      return this.documentService.save({ document: action.document}).pipe(
        switchMap((res: string) => {
          const result: Action[] =  [
            new documentActions.SaveSuccess(action.document.documentId, res, action.document.title),
          ];
            result.push(new commentActions.SaveSuccess( // comment is part of a different store
              {
                text: action.documentAddendum.text,
              }
            ));
          }
        catchError(err => of(new documentActions.SaveError()))
      );
    })
  );

thank you @stevieray8450 , I will try this next time.
Btw, what is the benefit of switchMap vs having something like
...
commentStore.dispatch( new commentActions.SaveSuccess(...))
return new documentActions.SaveSuccess(...)
...
?

is it "only" that there is no mixing of stores/modules, or is there some bigger problems that might occur?

Thanks!

@Ihatetomatoes hi, are the dispatched actions executed in the order?

new pizzaActions.LoadPizzasSuccess(pizzas),
new pizzaActions.AnotherAction(pizzas)

Is it guaranteed that LoadPizzasSuccess is executed before AnotherAction?

Thanks.

@Ihatetomatoes hi, are the dispatched actions executed in the order?

new pizzaActions.LoadPizzasSuccess(pizzas),
new pizzaActions.AnotherAction(pizzas)

Is it guaranteed that LoadPizzasSuccess is executed before AnotherAction?

Thanks.

Yes

edbzn commented

I noticed that it doesn't appear anywhere in documentation, maybe we could provide a small explanation with an example ?

@Ihatetomatoes Why do you need switchMap to return an array of actions? It doesn't look to me like anything else here needs flattening. Or is there more to what's going on here than just returning an array?

Returning more than one action from an effect is an anti-pattern

@portothree this might be true, but how should it be addressed then in practice?

Here is one typical scenario of multiple actions need to launch when an sample "SaveOrder" effect is finished:

  • UI needs to hide loading animation (layout store)
  • message needs to be shown to the user saying that the action is completed (communication store)
  • basket needs to be cleaned (basket store)
  • thank you page needs to be displayed (router)
    ....

Should this all be grouped in one action and one reducer will execute it? Or multiple reducers since it is multiple stores?
For application that I am working on right now, there are a lot of things that need to be repeated and grouped and organized into different cases, and the only logical thing for me is to have effect dispatch smaller individual actions based on well, its side effects.

@dakipro, I too read that its an anti pattern and I usually write multiple effects which listen on SaveOrderSuccess. I would love to know on how to achieve it without grouping actions

@sai-github What you suggested is the recommended pattern. Think of actions as more like events than commands. In the example above by @dakipro , the event is an order form was saved, dispatching the SaveOrder action. You can then have multiple effects listening for this action. You can have your layout store's effect to update the UI, a communication store effect to show the success message, etc. And remember, an effect can have multiple triggers. You don't need to create new effects for every event, you just need new actions.

Thank you guys, some time has passed since we started discussing and I think I was under the impression that en effect can only listen to actions from its own store. I will try your suggestions next time situation occurs.

Returning more than one action from an effect is an anti-pattern

How do you think it should be tackled.

@sai-github What you suggested is the recommended pattern. Think of actions as more like events than commands. In the example above by @dakipro , the event is an order form was saved, dispatching the SaveOrder action. You can then have multiple effects listening for this action. You can have your layout store's effect to update the UI, a communication store effect to show the success message, etc. And remember, an effect can have multiple triggers. You don't need to create new effects for every event, you just need new actions.

What if you need to guarantee order of execution?

@sai-github What you suggested is the recommended pattern. Think of actions as more like events than commands. In the example above by @dakipro , the event is an order form was saved, dispatching the SaveOrder action. You can then have multiple effects listening for this action. You can have your layout store's effect to update the UI, a communication store effect to show the success message, etc. And remember, an effect can have multiple triggers. You don't need to create new effects for every event, you just need new actions.

What if you need to guarantee order of execution?

Effects are merged and subscribed to the store, I don't know if there's a way to control the order of execution if you separate and only dispatch one action for each effect like we recommended here, maybe you can implement your own OnRunEffects interface and control the lifecycle. You can also create a 'chain' of actions related to a feature/subject:

  1. Dispatch SaveOrderSuccess
  2. Effect to SaveOrderSuccess dispatch HideOrderLoadingProgress
  3. Effect o HideOrderLoadingProgress dispatch CleanBasket
  4. Effect o CleanBasket dispatch ... And so on.

If your effect's requests are being canceled, it might be because of the way you're managing your streams with RxJS operators. Typically, this happens when operators like switchMap are used, as they cancel the previous request when a new one arrives. To ensure all requests are completed in sequence without being canceled, you can use concatMap instead of switchMap.