I am using Angular 6, ngrx/store. I have such effect that is responsible for updating things. Depends on some logic I want to dispatch different actions. What is the difference if I use switchMap
insted of map
?
This is what I tried but it doesn't work:
@Effect()
dispathMultipleActions$ = this.actions$.pipe(
ofType(ActionTypes.UpdateSomething),
map(() => {
const actions: Action[] = [];
const array = [1, 2, 3, 4, 5];
array.forEach(item => {
if (item > 3) {
actions.push(new DeleteAction(item));
} else {
actions.push(new ChangeAction(item));
}
});
return actions;
})
);
You need to run this command npm install @ngrx/effects — save to install required dependencies. You have to define the actual services to make the API calls. Finally, you need to register all the effects with the EffectsModules and import this module into your main module or feature module.
They listen for actions dispatched from ngrx/store, meaning that we can dispatch something like a LOAD Action and listen to this action into an effect executing a particular code. They isolate side effects from components, allowing for more pure components that select state and dispatch actions.
Ngrx actions are simple JSON objects that implement the Action interface provided by @ngrx: export interface Action { type: string; } The type property is a string used to uniquely identify your action to your application.
Most effects are straightforward: they receive a triggering action, perform a side effect, and return an Observable stream of another action which indicates the result is ready. NgRx effects will then automatically dispatch that action to trigger the reducers and perform a state change.
An effect transforms a stream of actions, so you have a stream of actions as input and output. In your example, you map an action to an array of actions. A stream of arrays of actions is not a valid output type. You need to flatten that array, meaning that you do not emit the array itself into the output stream but instead each of its elements.
Instead of:
input: --a-------a------>
output: --[b,c]---[b,c]-->
You should do:
input: --a-------a------>
output: --b-c-----b-c-->
For flattening an Observable of array into Observables of each element, you can use one of the operators mergeMap
, switchMap
, exhaustMap
. In most cases, mergeMap
will be the right choice. If you want to learn more about these operators, have a look at this answer.
@Effect()
register$: Observable<Action> = this.actions$.pipe(
ofType(AuthActionTypes.REGISTER_REQUEST),
mergeMap((action: RegisterRequest) => {
// check for register request success
return [
new RegisterSuccess(),
new LoginRequest(action.payload)
]
})
);
I have had same situation (and assuming NgRx 10 or higher), I have a different perspective, more fundamental way how to use effects. Triggering sequentially multiple actions in one place, specially within a single effect, is anti-pattern. In essense, its important to keep a consistent general flow of application state in NgRx of actions and potentials reductions. Just as the NgRx architecture foresees it.
Following the 3 effect rules will help already to avoid difficult situations:
That way, it helps you to follow the separation of concerns design pattern which of course also help you the NgRx effects to become a lot more unit testable.
Back to your example, you can simple decouple what you wanted to do (2 additional actions) with an in-between-proxy action.
In your case it seems that you may not need even your original effect dispathMultipleActions$, unless special logic within appears. (which perhaps, may belong into a state Reducer, which is even more unit testable).
Assuming that the ActionTypes.UpdateSomething has a array payload object already, you could split your dispathMultipleActions$ into single ones, so you could do something like this:
@Effect()
deleteAction$ = this.actions$.pipe(
ofType(ActionTypes.UpdateSomething),
concatMap(from(new Promise((array) => {
array.forEach(item => {
if (item > 3) {
//do something
}
});
}))),
{dispatch: false}
);
@Effect()
changeAction$ = this.actions$.pipe(
ofType(ActionTypes.UpdateSomething),
concatMap(from(new Promise((array) => {
array.forEach(item => {
if (item <= 3) {
//do something
}
});
}))),
{dispatch: false}
);
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