@ngrx Effect does not run the second time
Asked Answered
W

2

18

I've just started learning about @ngrx/store and @ngrx.effects and have created my first effect in my Angular/Ionic app. It runs ok the first time but if I dispatch the event to the store again (i.e when clicking the button again), nothing happens (no network call is made, nothing in console logs). Is there something obvious I'm doing wrong? Here's the effect:

@Effect() event_response$ = this.action$
    .ofType(SEND_EVENT_RESPONSE_ACTION)
    .map(toPayload)
    .switchMap((payload) => this.myService.eventResponse(payload.eventId,payload.response))
    .map(data => new SentEventResponseAction(data))
    .catch((error) => Observable.of(new ErrorOccurredAction(error)));

Thanks

Wattle answered 25/5, 2017 at 12:15 Comment(0)
M
43

It sounds like an error is occurring. In that situation, the action in the observable returned by catch will be emitted into the effect's stream and the effect will then complete - which will prevent the effect from running after the error action is emitted.

Move the map and the catch into the switchMap:

@Effect() event_response$ = this.action$
  .ofType(SEND_EVENT_RESPONSE_ACTION)
  .map(toPayload)
  .switchMap((payload) => this.myService
    .eventResponse(payload.eventId, payload.response)
    .map(data => new SentEventResponseAction(data))
    .catch((error) => Observable.of(new ErrorOccurredAction(error)))
);

Composing the catch within the switchMap will prevent the effect from completing if an error occurs.

Mythologize answered 25/5, 2017 at 12:20 Comment(6)
Does this mean using Angular's global error handler won't work with effects? #47896671Selfforgetful
This is still true with pipeable operators and catchError. Took me a while to find this question! Thank you.Amaya
For reference - you can do this without entering callback hell and just add a catchError at the end of the pipe: github.com/ngrx/platform/issues/646#issuecomment-398640097Propitiatory
@Adam Nice. However, I'd suggest returning caught.pipe(startWith(/* error action */)) instead of explicitly dispatching via the store - as done in the linked GitHub comment - which is not something I think should be done in an effect.Mythologize
@Mythologize - That's getting back into pipe's inside pipe's. Can you do: .pipe(...,catchError((err,caught) => caught),startWith(() => /* error action */)? I find it gross to do store.dispatch in an effect too, I just find it less gross than putting pipes inside pipes.Propitiatory
Unless I've misunderstood you, that will always start with your error action. IMO, explicitly dispatching via the store is a far greater sin. Effects are an abstraction over the store.Mythologize
C
5

You must move map() and catchError() into swithchMap() as following

@Effect()
public event_response$ = this.action$.pipe(
    ofType(SEND_EVENT_RESPONSE_ACTION),
    switchMap((payload) => {
        return this.myService.eventResponse(payload.eventId,payload.response).pipe(
            map((data: DataType) => new SentEventResponseAction(data)),
            catchError((error) => Observable.of(new ErrorOccurredAction(error)))
        })
    );
 );

Please note that, evetResponse() method inside myService should return an observable in order to use pipe afterward. In case your method inside service returns Promise, you can convert it into an observable by the use of from in the rxjs package as below:

import { from } from 'rxjs';
...
const promise = this.myService.eventResponse(payload.eventId,payload.response);
const observable = from(promise);
return observable.pipe(...

For more and detail description take a look at this link

Cosmography answered 7/8, 2019 at 13:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.