So I'm usually having a method that dispatches an action on click:
create() {
this.store.dispatch(new Create(this.form.value));
}
this code triggers the following scenario and dispatches a CreateSuccess or CreateFailed depending of if the request failed or not
@Action(Create)
create({ dispatch }: StateContext<StateInterface>, { entity}: Create) {
return this.http.post('mybackend', entity).pipe(
tap<EntityType>(resp => dispatch(new CreateSuccess(resp))),
catchError(error => dispatch(new CreateFailed(error)))
);
}
Now inside the Component where I called the create()
I'm listening for those two actions.
this.actions$.pipe(
ofActionSuccessful(CreateSuccess, UpdateSuccess),
takeUntil(componentDestroyed(this)) // <--
).subscribe(action => doStuff());
This all works flawlessly, the only thing that bothers me is that every time I use this, I have to add the takeUntil() part so the subscription is cancelled when the component is destroyed.
I know this is probably not a real issue for everyone, but I'd like to know if there is a cleaner way to do this.
I've thought about a custom RxJS operator that could handle this, but maybe there are other options, or (something I haven't found anything about), is there a way that NGXS does the unsubscribing itself?
In NGXS the dispatch method returns an observable that will complete once the action handling is complete (no unsubscribe necessary). So in your example your Create
action will complete when the handlers of the action have completed. If a handler returns an observable (like you have done) then the completion is linked to the completion of that observable. If you were to use a map
as opposed to a tap
for the dispatching of the CreateSuccess
action then the originating observable will wait for that 'child' observable to complete first.
I would structure the handler like this to make the Create
action complete only once the CreateSuccess
has completed:
@Action(Create)
create({ dispatch }: StateContext<StateInterface>, { entity}: Create) {
return this.http.post('mybackend', entity).pipe(
map<EntityType>(resp => dispatch(new CreateSuccess(resp))),
catchError(error => dispatch(new CreateFailed(error)))
);
}
Then in your component you can do this:
create() {
this.store.dispatch(new Create(this.form.value))
.subscribe(action => doStuff());
}
The observable would complete when the dispatched action's handling (and 'child' action handling) is completed. No unsubscription necessary.