Search code examples
angularrxjsngrx

difference between switchMap and other operators in my ngrx effect


I have following effect

  public SetProperTab$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(actions.SetProperTab),
        switchMap((action) =>
          this.store$.select(selectors.GetHasLogicalProductInStore, { id: action.logicalProductId }).pipe(
            switchMap((hasLogicalProductInStore) => {
              if (hasLogicalProductInStore) {
                return of(true);
              } else {
                return this.actions$.pipe(ofType(actions.GetLogicalProductDetailsSuccess), take(1));
              }
            }),
            concatLatestFrom(() =>
              this.store$.select(selectors.GetProperSpecifiedLogicalProduct, { logicalProductId: action.logicalProductId }),
            ),
            concatMap(([_, properCurrent]) => {
              if (properCurrent?.ProductsLite.length) {
                const lastProductConfig: RestProductLiteEntity | undefined = properCurrent.ProductsLite.at(-1);
                if (lastProductConfig) {
                  this.router.navigate([`/main/product-editor/${action.logicalProductId}/product-config/${lastProductConfig.ID}`]);
                }
              } else {
                this.router.navigate([`/main/product-editor/${action.logicalProductId}/global`]);
              }
              return EMPTY;
            }),
          ),
        ),
      );
    },
    { dispatch: false },
  );

What it basically does it just fires after users click on kendo-grid list. Then it checks if such item(and its large dataset) is already in store if not it is returning another pipe with ofType operator to make sure GetLogicalProductDetailsSuccess is completed(it happens after data is just downloaded from backend) then it exectues logic responsible for setting proper navigation and... the rest does not matter. Because it works as exptected.

But i cant understand in this case why changing first operator switchMap to exhasutMap or concatMap causes this effect uncallable. Well - it is called but only once(after selecting the first item) and after that effect is not reacting anymore despite this action below being dispatched all the time user click on list item...

public onSelectedChanged(value: ID | null): void {
    if (value) {
      this.productEditorService.onSelectedChanged(value);
      this.store$.dispatch(SetProperTab({ logicalProductId: value.toString() }));
    }

I would expect it to work the same as with switchMap... because the action is disptched only once after one click...


Solution

  • Here are the difference in short.

    switchMap cancels current inner subscription on new outer emitted values.

    concatMap puts all emitted value in a row, when done with current then it will pick the next value, this can backlog if out a lot of outer values are emitted.

    exhaustMap will ignore outer emitted values while inner subscription still active.

    Now based on the code you shared above i think you issue is the concatLatestFrom, that will continue to emit values if there is changes and then you concat those changes later with concatMap.