Reputation: 15654
I would like to dispatch two actions in one effect. Currently I have to declare two effects to achieve this :
// first effect
@Effect()
action1$ = this.actions$
.ofType(CoreActionTypes.MY_ACTION)
.map(res => {
return { type: "ACTION_ONE"}
})
.catch(() => Observable.of({
type: CoreActionTypes.MY_ACTION_FAILED
}));
// second effect
@Effect()
action2$ = this.actions$
.ofType(CoreActionTypes.MY_ACTION)
.map(res => {
return { type: "ACTION_TWO"}
})
.catch(() => Observable.of({
type: CoreActionTypes.MY_ACTION_FAILED
}));
Is it possible to have one action, be the source of two actions via a single effect?
Upvotes: 52
Views: 58683
Reputation: 17494
How about using flatMap
as below with ngrx 8.3.0
version syntax of effects
and actions
:
someEffect$ = createEffect(() =>
this.actions$.pipe(
ofType(CoreActionTypes.MY_ACTION),
flatMap((data) => [
CoreActionTypes.MY_ACTION_2(data),
CoreActionTypes.MY_ACTION_3(data),
])
)
);
Upvotes: 1
Reputation: 799
If anyone wonders how to mix plain actions with ones from Observables.
I was stuck with same task, but small difference: I needed to dispatch two actions, with second one after API call, which made it an Observable. Something like:
action1
is just an action: {type: 'ACTION_ONE'}
action2
is API call mapped to action: Observable<{type: 'ACTION_TWO'}>
Following code solved my issue:
@Effect() action$ = this.actions$.pipe(
ofType(CoreActionTypes.MY_ACTION),
mergeMap(res =>
// in order to dispatch actions in provided order
concat(
of(action1),
action2
)
),
catchError(() => Observable.of({
type: CoreActionTypes.MY_ACTION_FAILED
}))
);
Upvotes: 13
Reputation: 2095
@Effect()
loadInitConfig$ = this.actions$
.ofType(layout.ActionTypes.LOAD_INIT_CONFIGURATION)
.map<Action, void>(toPayload)
.switchMap(() =>
this.settingsService
.loadInitConfiguration()
.mergeMap((data: any) => [
new layout.LoadInitConfigurationCompleteAction(data.settings),
new meetup.LoadInitGeolocationCompleteAction(data.geolocation)
])
.catch(error =>
Observable.of(
new layout.LoadInitConfigurationFailAction({
error
})
)
)
);
Upvotes: 73
Reputation: 1431
you can also use .do() and store.next()
do allows you to attach a callback to the observable without affecting the other operators / changing the observable
e.g.
@Effect() action1$ = this.actions$
.ofType(CoreActionTypes.MY_ACTION)
.do(myaction => this.store.next( {type: 'ACTION_ONE'} ))
.switchMap((myaction) => Observable.of(
{type: 'ACTION_TWO'}
))
.catch(() => Observable.of({
type: CoreActionTypes.MY_ACTION_FAILED
}));
(you will need a reference to the store in your effects class)
Upvotes: 2
Reputation: 23793
You can use switchMap
and Observable.of
.
@Effect({ dispatch: true }) action$ = this.actions$
.ofType(CoreActionTypes.MY_ACTION)
.switchMap(() => Observable.of(
// subscribers will be notified
{type: 'ACTION_ONE'} ,
// subscribers will be notified (again ...)
{type: 'ACTION_TWO'}
))
.catch(() => Observable.of({
type: CoreActionTypes.MY_ACTION_FAILED
}));
Performance matters :
Instead of dispatching many actions that will trigger all the subscribers as many times as you dispatch, you may want to take a look into redux-batched-actions.
This allows you to warn your subscribers only when all of those multiple actions have been applied to the store.
For example :
@Effect({ dispatch: true }) action$ = this.actions$
.ofType(CoreActionTypes.MY_ACTION)
// subscribers will be notified only once, no matter how many actions you have
// not between every action
.map(() => batchActions([
doThing(),
doOther()
]))
.catch(() => Observable.of({
type: CoreActionTypes.MY_ACTION_FAILED
}));
Upvotes: 19
Reputation: 1692
You can choose mergeMap
for async or concatMap
for sync
@Effect() action$ = this.actions$
.ofType(CoreActionTypes.MY_ACTION)
.mergeMap(() => Observable.from([{type: "ACTION_ONE"} , {type: "ACTION_TWO"}]))
.catch(() => Observable.of({
type: CoreActionTypes.MY_ACTION_FAILED
}));
Upvotes: 6