javascript - Wie kann man mehrere Aktionen in ngrx/effect (redux-observable) auslösen?
Ich verwende Angular 6, ngrx/store. Ich habe einen Effekt, der dafür zuständig ist, Dinge zu aktualisieren. Abhängig von einiger Logik möchte ich verschiedene Aktionen auslösen. Was ist der Unterschied, wenn ich switchMap anstelle von map verwende?
Das ist, was ich versucht habe, aber es funktioniert nicht:
@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;
})
);
Antwort #1
Ein Effekt transformiert einen Strom von Aktionen, sodass Sie einen Strom von Aktionen als Eingabe und Ausgabe haben. In Ihrem Beispiel ordnen Sie einer Aktion ein Array von Aktionen zu. Ein Strom von Arrays von Aktionen ist kein gültiger Ausgabetyp. Sie müssen dieses Array auflösen, was bedeutet, dass Sie nicht das Array selbst in den Ausgabestrom senden, sondern jedes seiner Elemente.
Statt:
input: --a-------a------>
output: --[b,c]---[b,c]-->
Du solltest tun:
input: --a-------a------>
output: --b-c-----b-c-->
Um ein Observable von Arrays in Observables jedes Elements zu glätten, kannst du einen der Operatoren mergeMap, switchMap, exhaustMap verwenden. In den meisten Fällen wird mergeMap die richtige Wahl sein. Wenn du mehr über diese Operatoren erfahren möchtest, schau dir diese Antwort an.
@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)
]
})
);
Antwort #2
Ich habe die gleiche Situation erlebt (und unter der Annahme von NgRx 10 oder höher) und habe eine andere Perspektive, eine grundlegendere Art, Effects zu verwenden. Mehrere Aktionen nacheinander an einer Stelle auszulösen, besonders innerhalb eines einzelnen Effects, ist ein Anti-Pattern. Im Wesentlichen ist es wichtig, einen konsistenten allgemeinen Ablauf des Anwendungszustands in NgRx in Bezug auf Aktionen und mögliche Reduzierungen beizubehalten. Genau so, wie es die NgRx-Architektur vorsieht.
Die Einhaltung der 3 Wirkungsregeln wird bereits helfen, schwierige Situationen zu vermeiden:
- Name-Effekte sollen der Name des Effekts sein
- Lass deine Wirkung nur eine Sache tun
- Nur eine Aktion ausführen
Auf diese Weise hilft es Ihnen, dem Designmuster der Trennung der Anliegen zu folgen, was Ihnen natürlich auch dabei hilft, die NgRx-Effekte wesentlich besser unit-testbar zu machen.
Zurück zu Ihrem Beispiel, Sie können einfach das, was Sie tun wollten (2 zusätzliche Aktionen), mit einer Zwischen-Proxy-Aktion entkoppeln.
In Ihrem Fall scheint es, dass Sie möglicherweise nicht einmal Ihren ursprünglichen Effekt dispathMultipleActions$ benötigen, es sei denn, es taucht eine spezielle Logik auf. (die vielleicht in einen State-Reducer gehört, der noch leichter unit-testbar ist).
Wenn man annimmt, dass ActionTypes.UpdateSomething bereits ein Array-Payload-Objekt hat, könnte man sein dispathMultipleActions$ in einzelne Aktionen aufteilen, sodass man so etwas tun könnte:
@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}
);
