Ngrx und Angular 2 Tutorial: Erstellen einer reaktiven Anwendung
Wir sprechen viel über reaktive Programmierung im Angular-Bereich. Reaktive Programmierung und Angular 2 scheinen Hand in Hand zu gehen. Für alle, die nicht mit beiden Technologien vertraut sind, kann es jedoch eine ziemlich entmutigende Aufgabe sein, herauszufinden, worum es geht.In diesem Artikel erfahren Sie durch das Erstellen einer reaktiven Angular 2-Anwendung mit Ngrx, was das Muster ist, wo sich das Muster als nützlich erweisen kann und wie das Muster zum Erstellen besserer Angular 2-Anwendungen verwendet werden kann.
Ngrx ist eine Gruppe von Angular-Bibliotheken für reaktive Erweiterungen. Ngrx /Store implementiert das Redux-Muster unter Verwendung der bekannten RxJS-Observablen von Angular 2. Es bietet mehrere Vorteile, indem es den Anwendungsstatus auf einfache Objekte vereinfacht, den unidirektionalen Datenfluss erzwingt und vieles mehr. Die Ngrx / Effects-Bibliothek ermöglicht es der Anwendung, mit der Außenwelt zu kommunizieren, indem Nebenwirkungen ausgelöst werden.
Was ist reaktive Programmierung?
Reaktive Programmierung ist ein Begriff, den man heutzutage oft hört, aber was bedeutet das wirklich?
Reaktive Programmierung ist eine Art und Weise, wie Anwendungen Ereignisse und Datenflüsse in Ihren Anwendungen handhaben. Bei der reaktiven Programmierung entwerfen Sie Ihre Komponenten und andere Teile Ihrer Software, um auf diese Änderungen zu reagieren, anstatt nach Änderungen zu fragen. Dies kann eine große Verschiebung sein.
Ein großartiges Werkzeug für die reaktive Programmierung ist, wie Sie vielleicht wissen, RxJS.
Durch die Bereitstellung von Observablen und vielen Operatoren zur Transformation eingehender Daten hilft Ihnen diese Bibliothek bei der Behandlung von Ereignissen in Ihrer Anwendung. Tatsächlich können Sie mit Observables Ereignisse als einen Stream von Ereignissen und nicht als einmaliges Ereignis anzeigen. Auf diese Weise können Sie sie kombinieren, um beispielsweise ein neues Ereignis zu erstellen, das Sie anhören möchten.
Reaktive Programmierung ist eine Verschiebung in der Art und Weise, wie Sie zwischen verschiedenen Teilen einer Anwendung kommunizieren. Anstatt Daten direkt an die Komponente oder den Dienst zu senden, der sie benötigt, reagiert bei der reaktiven Programmierung die Komponente oder der Dienst auf Datenänderungen.
Ein Wort zu Ngrx
Um die Anwendung zu verstehen, die Sie in diesem Lernprogramm erstellen, müssen Sie kurz in die Kernkonzepte von Redux eintauchen.
Store
Der Store kann als Ihre clientseitige Datenbank angesehen werden, spiegelt aber vor allem den Status Ihrer Anwendung wider. Sie können es als die einzige Quelle der Wahrheit sehen.
Es ist das einzige, was Sie ändern, wenn Sie dem Redux-Muster folgen, und Sie ändern es, indem Sie Aktionen an es senden.
Reducer
Reducer sind die Funktionen, die wissen, was mit einer bestimmten Aktion und dem vorherigen Status Ihrer App zu tun ist.
Die Reduzierungen übernehmen den vorherigen Status aus Ihrem Speicher und wenden eine reine Funktion darauf an. Pure bedeutet, dass die Funktion immer denselben Wert für dieselbe Eingabe zurückgibt und keine Nebenwirkungen hat. Aus dem Ergebnis dieser reinen Funktion erhalten Sie einen neuen Status, der in Ihrem Geschäft gespeichert wird.
Aktionen
Aktionen sind die Nutzdaten, die die erforderlichen Informationen zum Ändern Ihres Shops enthalten. Grundsätzlich hat eine Aktion einen Typ und eine Nutzlast, die Ihre Reduziererfunktion verwendet, um den Status zu ändern.
Dispatcher
Dispatcher sind einfach ein Einstiegspunkt für Sie, um Ihre Aktion zu versenden. In Ngrx gibt es eine Versandmethode direkt im Geschäft.
Middleware
Middleware sind einige Funktionen, die jede ausgelöste Aktion abfangen, um Nebenwirkungen zu erzeugen, obwohl Sie sie in diesem Artikel nicht verwenden werden. Sie sind in der Ngrx / Effect-Bibliothek implementiert, und es besteht eine große Chance, dass Sie sie beim Erstellen realer Anwendungen benötigen.
Warum Ngrx verwenden?
Komplexität
Der Speicher und der unidirektionale Datenfluss reduzieren die Kopplung zwischen Teilen Ihrer Anwendung erheblich. Diese reduzierte Kopplung reduziert die Komplexität Ihrer Anwendung, da sich jedes Teil nur um bestimmte Zustände kümmert.
Werkzeuge
Der gesamte Status Ihrer Anwendung wird an einem Ort gespeichert, sodass Sie auf einfache Weise einen globalen Überblick über den Status Ihrer Anwendung erhalten und während der Entwicklung hilfreich sind. Mit Redux kommen auch viele nette Entwicklertools, die den Store nutzen und dazu beitragen können, einen bestimmten Status der Anwendung zu reproduzieren oder beispielsweise Zeitreisen zu machen.
Architektonische Einfachheit
Viele der Vorteile von Ngrx sind mit anderen Lösungen erreichbar; schließlich ist Redux ein architektonisches Muster. Wenn Sie jedoch eine Anwendung erstellen müssen, die sich hervorragend für das Redux-Muster eignet, z. B. kollaborative Bearbeitungswerkzeuge, können Sie ganz einfach Funktionen hinzufügen, indem Sie dem Muster folgen.
Obwohl Sie nicht darüber nachdenken müssen, was Sie tun, wird das Hinzufügen einiger Dinge wie Analysen in all Ihren Anwendungen trivial, da Sie alle Aktionen verfolgen können, die gesendet werden.
Kleine Lernkurve
Da dieses Muster so weit verbreitet und einfach ist, ist es für neue Leute in Ihrem Team wirklich einfach, schnell nachzuholen, was Sie getan haben.
Ngrx glänzt am meisten, wenn Sie viele externe Akteure haben, die Ihre Anwendung ändern können, z. B. ein Monitoring-Dashboard. In diesen Fällen ist es schwierig, alle eingehenden Daten zu verwalten, die an Ihre Anwendung gesendet werden, und die Statusverwaltung wird schwierig. Deshalb möchten Sie es mit einem unveränderlichen Zustand vereinfachen, und dies ist eine Sache, die uns der Ngrx-Store bietet.
Erstellen einer Anwendung mit Ngrx
Die Leistung von Ngrx zeigt sich am besten, wenn Sie externe Daten haben, die in Echtzeit an unsere Anwendung gesendet werden. Lassen Sie uns in diesem Sinne ein einfaches Freelancer-Raster erstellen, das Online-Freelancer anzeigt und es Ihnen ermöglicht, durch sie zu filtern.
Einrichten des Projekts
Angular CLI ist ein großartiges Tool, das den Einrichtungsprozess erheblich vereinfacht. Vielleicht möchten Sie es nicht verwenden, aber denken Sie daran, dass der Rest dieses Artikels es verwenden wird.
npm install -g @angular/cli
Als nächstes möchten Sie eine neue Anwendung erstellen und alle Ngrx-Bibliotheken installieren:
ng new toptal-freelancersnpm install ngrx --save
Freelancer Reduzierer
Reduzierer sind ein Kernstück der Redux-Architektur. Warum also nicht zuerst mit ihnen beginnen, während Sie die Anwendung erstellen?
Erstellen Sie zunächst einen „Freelancer“ -Reduzierer, der jedes Mal für die Erstellung unseres neuen Status verantwortlich ist, wenn eine Aktion an den Store gesendet wird.
freelancer-grid/Freiberufler.Druckminderer.ts
import { Action } from '@ngrx/store';export interface AppState { freelancers : Array<IFreelancer>}export interface IFreelancer { name: string, email: string, thumbnail: string}export const ACTIONS = { FREELANCERS_LOADED: 'FREELANCERS_LOADED',}export function freelancersReducer( state: Array<IFreelancer> = , action: Action): Array<IFreelancer> { switch (action.type) { case ACTIONS.FREELANCERS_LOADED: // Return the new state with the payload as freelancers list return Array.prototype.concat(action.payload); default: return state; }}
Also hier ist unser freelancers Reducer.
Diese Funktion wird jedes Mal aufgerufen, wenn eine Aktion über den Store gesendet wird. Wenn die Aktion FREELANCERS_LOADED
lautet, wird aus der Aktionsnutzlast ein neues Array erstellt. Wenn dies nicht der Fall ist, wird die alte Statusreferenz zurückgegeben und nichts wird angehängt.
Es ist wichtig zu beachten, dass, wenn die alte Statusreferenz zurückgegeben wird, der Status als unverändert betrachtet wird. Das bedeutet, wenn Sie eine state.push(something)
aufrufen, wird der Status nicht als geändert betrachtet. Denken Sie daran, während Sie Ihre Reduziererfunktionen ausführen.
Zustände sind unveränderlich. Bei jeder Änderung muss ein neuer Status zurückgegeben werden.
Freelancer Grid Component
Erstellen Sie eine Grid-Komponente, um unsere Online-Freelancer anzuzeigen. Zunächst spiegelt es nur wider, was sich im Laden befindet.
ng generate component freelancer-grid
Fügen Sie Folgendes in das freelancer-Grid ein.Komponente.ts
import { Component, OnInit } from '@angular/core';import { Store } from '@ngrx/store';import { AppState, IFreelancer, ACTIONS } from './freelancer-reducer';import * as Rx from 'RxJS';@Component({ selector: 'app-freelancer-grid', templateUrl: './freelancer-grid.component.html', styleUrls: ,})export class FreelancerGridComponent implements OnInit { public freelancers: Rx.Observable<Array<IFreelancer>>; constructor(private store: Store<AppState>) { this.freelancers = store.select('freelancers'); }}
Und das Folgende im Freelancer-grid.Komponente.html:
<span class="count">Number of freelancers online: {{(freelancers | async).length}}</span><div class="freelancer fade thumbail" *ngFor="let freelancer of freelancers | async"> <button type="button" class="close" aria-label="Close" (click)="delete(freelancer)"><span aria-hidden="true">×</span></button><br> <img class="img-circle center-block" src="{{freelancer.thumbnail}}" /><br> <div class="info"><span><strong>Name: </strong>{{freelancer.name}}</span> <span><strong>Email: </strong>{{freelancer.email}}</span></div> <a class="btn btn-default">Hire {{freelancer.name}}</a></div>
Also, was hast du gerade getan?
Zuerst haben Sie eine neue Komponente mit dem Namen freelancer-grid
erstellt.
Die Komponente enthält eine Eigenschaft mit dem Namen freelancers
, die Teil des Anwendungsstatus ist, der im Ngrx-Speicher enthalten ist. Wenn Sie den select-Operator verwenden, werden Sie nur über die freelancers
-Eigenschaft des gesamten Anwendungsstatus benachrichtigt. Jedes Mal, wenn sich die freelancers
Eigenschaft des Anwendungsstatus ändert, wird Ihr Observable benachrichtigt.
Das Schöne an dieser Lösung ist, dass Ihre Komponente nur eine Abhängigkeit hat und es der Speicher ist, der Ihre Komponente viel weniger komplex und leicht wiederverwendbar macht.
Auf dem Template-Teil haben Sie nichts zu komplex gemacht. Beachten Sie die Verwendung von async Pipe in der *ngFor
. Das freelancers
observable ist nicht direkt iterierbar, aber dank Angular verfügen wir über die Tools, um es zu entpacken und das dom mithilfe der async Pipe an seinen Wert zu binden. Dies macht die Arbeit mit dem Observable so viel einfacher.
Hinzufügen der Funktion Freelancer entfernen
Nachdem Sie nun eine funktionale Basis haben, fügen wir der Anwendung einige Aktionen hinzu.
Sie möchten einen Freelancer aus dem Status entfernen können. Je nachdem, wie Redux funktioniert, müssen Sie diese Aktion zuerst in jedem Status definieren, der davon betroffen ist.
In diesem Fall ist es nur der freelancers
Reduzierer:
export const ACTIONS = { FREELANCERS_LOADED: 'FREELANCERS_LOADED', DELETE_FREELANCER: 'DELETE_FREELANCER',}export function freelancersReducer( state: Array<IFreelancer> = , action: Action): Array<IFreelancer> { switch (action.type) { case ACTIONS.FREELANCERS_LOADED: // Return the new state with the payload as freelancers list return Array.prototype.concat(action.payload); case ACTIONS.DELETE_FREELANCER: // Remove the element from the array state.splice(state.indexOf(action.payload), 1); // We need to create another reference return Array.prototype.concat(state); default: return state; }}
Hier ist es wirklich wichtig, aus dem alten ein neues Array zu erstellen, um einen neuen unveränderlichen Zustand zu erhalten.
Jetzt können Sie Ihrer Komponente eine Funktion zum Löschen von Freelancern hinzufügen, die diese Aktion an den Store sendet:
delete(freelancer) { this.store.dispatch({ type: ACTIONS.DELETE_FREELANCER, payload: freelancer, }) }
Sieht das nicht einfach aus?
Sie können jetzt einen bestimmten Freelancer aus dem Status entfernen, und diese Änderung wird durch Ihre Anwendung weitergegeben.Was ist nun, wenn Sie der Anwendung eine weitere Komponente hinzufügen, um zu sehen, wie sie über den Store miteinander interagieren können?
Filterreduzierer
Beginnen wir wie immer mit dem Reduzierer. Für diese Komponente ist es ganz einfach. Sie möchten, dass der Reduzierer immer einen neuen Status mit nur der von uns versendeten Eigenschaft zurückgibt. Es sollte so aussehen:
import { Action } from '@ngrx/store';export interface IFilter { name: string, email: string,}export const ACTIONS = { UPDATE_FITLER: 'UPDATE_FITLER', CLEAR_FITLER: 'CLEAR_FITLER',}const initialState = { name: '', email: '' };export function filterReducer( state: IFilter = initialState, action: Action): IFilter { switch (action.type) { case ACTIONS.UPDATE_FITLER: // Create a new state from payload return Object.assign({}, action.payload); case ACTIONS.CLEAR_FITLER: // Create a new state from initial state return Object.assign({}, initialState); default: return state; }}
Filterkomponente
import { Component, OnInit } from '@angular/core';import { IFilter, ACTIONS as FilterACTIONS } from './filter-reducer';import { Store } from '@ngrx/store';import { FormGroup, FormControl } from '@angular/forms';import * as Rx from 'RxJS';@Component({ selector: 'app-filter', template: '<form class="filter">'+ '<label>Name</label>'+ '<input type="text" ="name" name="name"/>'+ '<label>Email</label>'+ '<input type="text" ="email" name="email"/>'+ '<a (click)="clearFilter()" class="btn btn-default">Clear Filter</a>'+ '</form>', styleUrls: ,})export class FilterComponent implements OnInit { public name = new FormControl(); public email = new FormControl(); constructor(private store: Store<any>) { store.select('filter').subscribe((filter: IFilter) => { this.name.setValue(filter.name); this.email.setValue(filter.email); }) Rx.Observable.merge(this.name.valueChanges, this.email.valueChanges).debounceTime(1000).subscribe(() => this.filter()); } ngOnInit() { } filter() { this.store.dispatch({ type: FilterACTIONS.UPDATE_FITLER, payload: { name: this.name.value, email: this.email.value, } }); } clearFilter() { this.store.dispatch({ type: FilterACTIONS.CLEAR_FITLER, }) }}
Zunächst haben Sie eine einfache Vorlage erstellt, die ein Formular mit zwei Feldern (Name und E-Mail) enthält, das unseren Status widerspiegelt.
Sie halten diese Felder mit dem Status etwas anders synchron als mit dem freelancers
Status. Wie Sie gesehen haben, haben Sie den Filterstatus abonniert, und jedes Mal wird der neue Wert dem formControl
zugewiesen.
Eine Sache, die mit Angular 2 nett ist, ist, dass es Ihnen viele Werkzeuge zur Verfügung stellt, um mit Observablen zu interagieren.
Sie haben die async Pipe früher gesehen, und jetzt sehen Sie die formControl
Klasse, mit der Sie den Wert einer Eingabe beobachten können. Dies ermöglicht ausgefallene Dinge wie das, was Sie in der Filterkomponente getan haben.
Wie Sie sehen können, verwenden Sie Rx.observable.merge
, um die beiden Observablen zu kombinieren, die von Ihrer formControls
, und entprellen Sie diese neue observable , bevor Sie die filter
-Funktion auslösen.
In einfacheren Worten, Sie warten eine Sekunde, nachdem sich Name oder E-Mail formControl
geändert haben, und rufen dann die Funktion filter
auf.
Ist das nicht toll?
All dies geschieht in wenigen Zeilen Code. Dies ist einer der Gründe, warum Sie RxJS lieben werden. Es ermöglicht Ihnen, viele dieser ausgefallenen Dinge leicht zu machen, die sonst komplizierter gewesen wären.
Kommen wir nun zu dieser Filterfunktion. Was macht es?
Es sendet einfach die UPDATE_FILTER
Aktion mit dem Wert des Namens und der E-Mail, und der Reduzierer kümmert sich darum, den Status mit diesen Informationen zu ändern.
Kommen wir zu etwas Interessanterem.
Wie lässt du diesen Filter mit deinem zuvor erstellten Freelancer-Grid interagieren?
Einfach. Sie müssen nur den Filterteil des Geschäfts anhören. Mal sehen, wie der Code aussieht.
import { Component, OnInit } from '@angular/core';import { Store } from '@ngrx/store';import { AppState, IFreelancer, ACTIONS } from './freelancer-reducer';import { IFilter, ACTIONS as FilterACTIONS } from './../filter/filter-reducer';import * as Rx from 'RxJS';@Component({ selector: 'app-freelancer-grid', templateUrl: './freelancer-grid.component', styleUrls: ,})export class FreelancerGridComponent implements OnInit { public freelancers: Rx.Observable<Array<IFreelancer>>; public filter: Rx.Observable<IFilter>; constructor(private store: Store<AppState>) { this.freelancers = Rx.Observable.combineLatest(store.select('freelancers'), store.select('filter'), this.applyFilter); } applyFilter(freelancers: Array<IFreelancer>, filter: IFilter): Array<IFreelancer> { return freelancers .filter(x => !filter.name || x.name.toLowerCase().indexOf(filter.name.toLowerCase()) !== -1) .filter(x => !filter.email || x.email.toLowerCase().indexOf(filter.email.toLowerCase()) !== -1) } ngOnInit() { } delete(freelancer) { this.store.dispatch({ type: ACTIONS.DELETE_FREELANCER, payload: freelancer, }) }}
Es ist nicht komplizierter als das.
Wieder einmal haben Sie die Leistungsfähigkeit von RxJS genutzt, um den Status Filter und Freelancer zu kombinieren.
Tatsächlich wird combineLatest
ausgelöst, wenn eine der beiden Observablen ausgelöst wird, und dann jeder Zustand mit der Funktion applyFilter
kombiniert. Es gibt ein neues Observable zurück, das dies tut. Wir müssen keine anderen Codezeilen ändern.
Beachten Sie, dass die Komponente sich nicht darum kümmert, wie der Filter abgerufen, geändert oder gespeichert wird. Wir haben gerade die Filterfunktionalität hinzugefügt und keine neuen Abhängigkeiten hinzugefügt.
Making It Shine
Denken Sie daran, dass die Verwendung von Ngrx wirklich glänzt, wenn wir mit Echtzeitdaten zu tun haben? Fügen wir diesen Teil unserer Anwendung hinzu und sehen Sie, wie es geht.
Einführung in die freelancers-service
.
ng generate service freelancer
Der Freelancer-Dienst simuliert den Echtzeitbetrieb von Daten und sollte so aussehen.
import { Injectable } from '@angular/core';import { Store } from '@ngrx/store';import { AppState, IFreelancer, ACTIONS } from './freelancer-grid/freelancer-reducer';import { Http, Response } from '@angular/http';@Injectable()export class RealtimeFreelancersService { private USER_API_URL = 'https://randomuser.me/api/?results=' constructor(private store: Store<AppState>, private http: Http) { } private toFreelancer(value: any) { return { name: value.name.first + ' ' + value.name.last, email: value.email, thumbail: value.picture.large, } } private random(y) { return Math.floor(Math.random() * y); } public run() { this.http.get(`${this.USER_API_URL}51`).subscribe((response) => { this.store.dispatch({ type: ACTIONS.FREELANCERS_LOADED, payload: response.json().results.map(this.toFreelancer) }) }) setInterval(() => { this.store.select('freelancers').first().subscribe((freelancers: Array<IFreelancer>) => { let getDeletedIndex = () => { return this.random(freelancers.length - 1) } this.http.get(`${this.USER_API_URL}${this.random(10)}`).subscribe((response) => { this.store.dispatch({ type: ACTIONS.INCOMMING_DATA, payload: { ADD: response.json().results.map(this.toFreelancer), DELETE: new Array(this.random(6)).fill(0).map(() => getDeletedIndex()), } }); this.addFadeClassToNewElements(); }); }); }, 10000); } private addFadeClassToNewElements() { let elements = window.document.getElementsByClassName('freelancer'); for (let i = 0; i < elements.length; i++) { if (elements.item(i).className.indexOf('fade') === -1) { elements.item(i).classList.add('fade'); } } }}
Dieser Dienst ist nicht perfekt, aber er macht, was er tut, und zu Demonstrationszwecken können wir einige Dinge demonstrieren.
Erstens ist dieser Service recht einfach. Es fragt eine Benutzer-API ab und überträgt die Ergebnisse in den Store. Es ist ein Kinderspiel, und Sie müssen nicht darüber nachdenken, wohin die Daten gehen. Es geht in den Laden, was Redux gleichzeitig so nützlich und gefährlich macht — aber wir werden später darauf zurückkommen. Nach jeweils zehn Sekunden wählt der Dienst einige Freiberufler aus und sendet einen Vorgang zum Löschen zusammen mit einem Vorgang an einige andere Freiberufler.
Wenn wir möchten, dass unser Reduzierer damit umgehen kann, müssen wir ihn ändern:
import { Action } from '@ngrx/store';export interface AppState { freelancers : Array<IFreelancer>}export interface IFreelancer { name: string, email: string,}export const ACTIONS = { LOAD_FREELANCERS: 'LOAD_FREELANCERS', INCOMMING_DATA: 'INCOMMING_DATA', DELETE_FREELANCER: 'DELETE_FREELANCER',}export function freelancersReducer( state: Array<IFreelancer> = , action: Action): Array<IFreelancer> { switch (action.type) { case ACTIONS.INCOMMING_DATA: action.payload.DELETE.forEach((index) => { state.splice(state.indexOf(action.payload), 1); }) return Array.prototype.concat(action.payload.ADD, state); case ACTIONS.FREELANCERS_LOADED: // Return the new state with the payload as freelancers list return Array.prototype.concat(action.payload); case ACTIONS.DELETE_FREELANCER: // Remove the element from the array state.splice(state.indexOf(action.payload), 1); // We need to create another reference return Array.prototype.concat(state); default: return state; }}
Jetzt können wir solche Operationen ausführen.
Eine Sache, die in diesem Dienst demonstriert wird, ist, dass es bei allen synchron durchgeführten Zustandsänderungen sehr wichtig ist, dies zu beachten. Wenn die Anwendung des Status async wäre, würde der Aufruf von this.addFadeClassToNewElements();
nicht funktionieren, da das DOM-Element beim Aufruf dieser Funktion nicht erstellt würde.
Ich persönlich finde das sehr nützlich, da es die Vorhersagbarkeit verbessert.
Anwendungen reaktiv erstellen
In diesem Tutorial haben Sie eine reaktive Anwendung mit Ngrx, RxJS und Angular 2 erstellt.
Wie Sie gesehen haben, sind dies mächtige Werkzeuge. Was Sie hier erstellt haben, kann auch als Implementierung einer Redux-Architektur angesehen werden, und Redux ist an sich mächtig. Es hat jedoch auch einige Einschränkungen. Während wir Ngrx verwenden, spiegeln sich diese Einschränkungen unweigerlich in dem Teil unserer Anwendung wider, den wir verwenden.
Das obige Diagramm ist eine grobe Darstellung der Architektur, die Sie gerade erstellt haben.
Sie werden feststellen, dass einige Komponenten, auch wenn sie sich gegenseitig beeinflussen, unabhängig voneinander sind. Dies ist eine Besonderheit dieser Architektur: Komponenten haben eine gemeinsame Abhängigkeit, nämlich den Speicher.
Eine weitere Besonderheit dieser Architektur ist, dass wir keine Funktionen aufrufen, sondern Aktionen versenden. Eine Alternative zu Ngrx könnte darin bestehen, nur einen Dienst zu erstellen, der einen bestimmten Status mit Observablen Ihrer Anwendungen verwaltet und Funktionen für diesen Dienst anstelle von Aktionen aufruft. Auf diese Weise könnten Sie die Zentralisierung und Reaktivität des Staates erreichen und gleichzeitig den problematischen Staat isolieren. Dieser Ansatz kann Ihnen helfen, den Aufwand beim Erstellen eines Reduzierers zu reduzieren und Aktionen als einfache Objekte zu beschreiben.