I have a service that makes http requests to a backend I don't control to get marketing page contents. Sometimes, I need to load more than one piece of marketing content at the same time. I can create an effect that calls the service.
@Effect()
marketingContent$ = this.actions$
.ofType(LOAD_MARKETING_CONTENT)
.switchMap(({ payload }) => this.marketingService.getContent(payload)
.map(content => Action.LoadMarketingContentComplete(content))
)
This works fine, and I can call store.dispatch(Action.LoadMarketingContent('A'))
.
The problem is that if I need to load more than one piece of marketing content at a time the .switchMap
will cancel the previous request.
store.dispatch(Action.LoadMarketingContent('A'));
store.dispatch(Action.LoadMarketingContent('B'));
// Only `'B'` is loaded since 'A' gets canceled before it completes
I can use .mergeMap
instead of .switchMap
, but then duplicate requests won't get canceled.
I can also use separate actions to load each piece of marketing content, but that would require creating an action and effects for each piece.
Is there a way that I can use .switchMap
to cancel only requests for the same content (where payload
is the same?) or another way to make simultaneous different requests while canceling duplicate requests in the same stream?
If you introduce a CANCEL_MARKETING_CONTENT
action you could do something like this with mergeMap
:
@Effect()
marketingContent$ = this.actions$
.ofType(LOAD_MARKETING_CONTENT)
.mergeMap(({ payload }) => this.marketingService
.getContent(payload)
.map(content => Action.LoadMarketingContentComplete(content))
.takeUntil(this.actions$.ofType(CANCEL_MARKETING_CONTENT))
);
Basically, that would let you load as many pieces of marketing content as you like, but it would be up to you to cancel any pending loads by dispatching an CANCEL_MARKETING_CONTENT
action before you dispatch the LOAD_MARKETING_CONTENT
action(s).
For example, to load only piece A
, you'd do this:
store.dispatch(Action.CancelMarketingContent());
store.dispatch(Action.LoadMarketingContent('A'));
And to load both pieces A
and B
, you'd do this:
store.dispatch(Action.CancelMarketingContent());
store.dispatch(Action.LoadMarketingContent('A'));
store.dispatch(Action.LoadMarketingContent('B'));
Actually, there is a similar - but neater - way of doing it and it doesn't involve using another action.
You can use the dispatch of the same action with the same payload as the cancellation trigger. For example:
@Effect()
marketingContent$ = this.actions$
.ofType(LOAD_MARKETING_CONTENT)
.mergeMap(({ payload }) => this.marketingService
.getContent(payload)
.map(content => Action.LoadMarketingContentComplete(content))
.takeUntil(this.actions$
.ofType(LOAD_MARKETING_CONTENT)
.skip(1)
.filter(({ payload: next }) => next === payload)
)
);
From memory, skip
will be needed to skip the action currently being handled by the effect. And the answer assumes that payload
is "A"
or "B"
, etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With