NgRx Effects; Dispatching an Action from CatchError, while also returning the Error itself

69 views Asked by At

I have an effect which catches the HttpErrorResponse, but since there's also a global error handler, I dont want to catch the error, I want to tap it.

The .catchError should both return the Error for the Interceptor, as well as dispatch the addItemFailure action

addItem$ = createEffect((): Observable<Action> => {
  return this.action$.pipe(
    ofType(ItemActions.addItem),
    exhaustMap((action) => {
      return this.apiClient.addItem(action.itemModel).pipe(
        map((response) => ItemActions.addItemSuccess({ response })),
        catchError((exception: HttpErrorResponse) => {
          return throwError(() => exception);
          return of(ItemActions.addItemFailure({ exception }));
        }),
      );
    }),
  );
});

Above of course does not work because of the double return, but it shows what I'm trying to achieve. Using one of them works, but I need them both to work.

I've tried:

catchError((exception: HttpErrorResponse) => {
  this.store.dispatch(ItemActions.addItemFailure({ exception }));
  return throwError(() => exception);
}),

or

catchError((exception: HttpErrorResponse) => {
  return throwError(() => {
    this.store.dispatch(ItemActions.addItemFailure({ exception }));
    return exception;
  });
})

But that doesn't work somehow.

2

There are 2 answers

0
Jeffrey Roosendaal On BEST ANSWER

Got it working with a little help from @ali-ataf:

catchError((exception: HttpErrorResponse) => {
  of(EMPTY).pipe(
    tap(() => this.store.dispatch(ItemActions.addItemFailure({ exception }))),
    switchMap(() => throwError(() => exception)),
  )
)

Full Effect now looks like:

addItem$ = createEffect((): Observable<Action> => {
  return this.action$.pipe(
    ofType(ItemActions.addItem),
    exhaustMap((action) => {
      return this.apiClient.addItem(action.itemModel).pipe(
        map((response) => ItemActions.addItemSuccess({ response })),
        catchError((exception: HttpErrorResponse) => {
          of(EMPTY).pipe(
            tap(() => this.store.dispatch(ItemActions.addItemFailure({ exception }))),
            switchMap(() => throwError(() => exception)),
          ),
        }),
      );
    }),
  );
});
3
Ali Ataf On

You can try chaining the 2 observables you want to execute as follows:

addItem$ = createEffect((): Observable<Action> => {
  return this.action$.pipe(
    ofType(ItemActions.addItem),
    exhaustMap((action) => {
      return this.apiClient.addItem(action.itemModel).pipe(
        map((response) => ItemActions.addItemSuccess({ response })),
        catchError((exception: HttpErrorResponse) => {
          return of(ItemActions.addItemFailure({ exception })).pipe(
            first(),
            switchMap(() => throwError(() => exception))
          );
        }),
      );
    }),
  );
});