React und RxJS sind eine tolle Kombination für die komponentenübergreifende Kommunikation, vor allem in großen Projekten. Der Nachteil ist, dass nicht für jeden Frontend-Entwickler Observables, Subjects und das erfolgreiche Emittieren von Ereignissen schnell umzusetzen sind, einfach weil RxJS so viel zu bieten hat. Deshalb haben wir diesen Artikel geschrieben, um Ihnen einige leicht verständliche und vollständig wiederverwendbare Code-Beispiele zu zeigen, die Ihnen helfen, ein Verständnis für diese robuste Bibliothek zu bekommen.
Komplexe Anforderungen an Webanwendungen fordern Entwickler heraus, andere Methoden der komponentenübergreifenden Kommunikation und asynchronen Datenmanipulation zu verwenden. Wenn Sie ein React-Entwickler sind, ist eine der besten Möglichkeiten, all das oben Genannte zu erreichen und gleichzeitig eine robuste und hochleistungsfähige Webanwendung zu haben, die Verwendung von React mit RxJS. RxJS basiert auf einem Programmierparadigma, das als reaktive Programmierung bekannt ist, ein Konzept, bei dem Code auf der Grundlage von Ereignissen geschrieben wird. Aus diesem Grund führt RxJS Schlüsselbegriffe wie Observables, Observers und Subjects ein, um eine effiziente und elegante Callback-basierte Ereignisbehandlung zu erreichen.
Um die drei oben genannten Schlüsselbegriffe kurz zu erklären:
Der beste Weg, um zu zeigen, wie React mit RxJS funktioniert, ist eine praktische Demo-Anwendung, die wir durchgehen wollen. Beachten Sie, dass sie eingebaute React Hooks enthält, ein Muster, mit dem die meisten React-Entwickler bereits vertraut sind und das die Observable-basierte Eventbehandlung mit RxJS zu einem ordentlichen und performanten Paket zusammenfasst.
Wir bei SABO bevorzugen den Einsatz von RxJS, wenn unsere Projekte erfordern, dass unabhängige Komponenten auf eine beliebige Anzahl von gemeinsam genutzten Ereignissen hören. Dies ermöglicht es uns, unseren Kunden eine saubere, aber dennoch erstaunliche und leistungsstarke komponentenübergreifende Kommunikation zu bieten. Da wir nicht wirklich in der Lage sind, Details unserer Kundenprojekte zu teilen, möchten wir unseren Ansatz anhand eines einfachen Beispiels demonstrieren. In unserer Wecker-App sieht der Benutzer sowohl den Countdown als auch den Status auf dem Bildschirm. Es gibt zwei Schaltflächen: Snooze (verzögert den Alarm um weitere 5 Sekunden) und Dismiss (schaltet den Countdown ab und wünscht einen schönen Tag).
Die gerenderte React-Komponente sieht dann so aus:
{% c-block language="js" %}
function App() {
return (
<>
<h3>{"Alarm Clock ⏰"}</h3>
<StatusIndicator observable$={observable$} />
<Actions actions$={actions$} />
</>
);
}
})
{% c-block-end %}
Obwohl das Endprodukt einfach und für ein React mit RxJS-Projekt mit nur einer Datei machbar ist, werden wir es für die Zwecke der komponentenübergreifenden Kommunikation aufteilen. Beachten Sie die folgende Struktur:
Unsere Stammkomponente (index.js) importiert mehrere benutzerdefinierte Komponenten aus der Datei components/index.js, den StatusIndicator und die Komponente Actions. Darüber hinaus sehen wir mehrere RxJS-Pakete und Operatoren, die zur Bearbeitung von Ereignissen und Werten verwendet werden, die von einem RxJS Subject ausgelöst werden. Um die Handhabung zu vereinfachen, werden alle RxJS Konstanten außerhalb der funktionalen React Komponente definiert.
Die ersten drei Zeilen definieren ein RxJS Subject und weisen den Observables die Aktionen Snooze und Dismiss zu. Direkt danach definieren wir ein Countdown$ Observable, das alle n Millisekunden ein Ereignis auslöst (wie durch die RxJS-Funktion interval(n, () => {}) definiert), das wir verwenden, um den Timer um 1 Sekunde zu verringern und das ausgelöste Ereignis dann an alle Abonnenten zu teilen() oder zu multicasten.
Der snoozableAlarm$ ist ein weiteres Observable, das wir verwenden, um das zuvor definierte countdown$ Observable zu abonnieren. Bei Erreichen von 0 wird die Nachricht "Wake up!" ausgelöst. Der snoozableAlarm$ Observable fügt auch snooze$ zur Pipe hinzu, so dass er das Anklicken des Snooze-Buttons verarbeiten kann, wodurch der Zähler wieder auf 5 Sekunden zurückgesetzt wird.
Schließlich wird die Haupt-Observable, die wir observable$ nennen, snoozableAlarm$ abonnieren und darauf hören, bis die Observable dismiss$ ausgelöst wird (dies geschieht, sobald wir auf die Schaltfläche Dismiss klicken). Diese RxJS Observable ist diejenige, die wir in unserer React-Komponente abonnieren werden.
{% c-block language="js" %}
const actions$ = new Subject();
const snooze$ = actions$.pipe(filter((action) => action === "snooze"));
const dismiss$ = actions$.pipe(filter((action) => action === "dismiss"));
const countdown$ = interval(1000).pipe(
// Define a starting value
startWith(5),
// Emit the current accumulation state after each update
scan((time) => time - 1),
// Emit values as long as each value satisfies the criteria
takeWhile((time) => time > 0),
// Multicast
share()
);
const snoozeableAlarm$ = concat(countdown$, of("Wake up! 🎉")).pipe(
// Re-subscribe to the snooze$ Observable once it triggers
repeatWhen(() => snooze$)
);
const observable$ = concat(
// Emit further values until the dismiss$ Observable emits a value
snoozeableAlarm$.pipe(takeUntil(dismiss$)),
of("Have a nice day! ☀️")
);
{% c-block-end %}
Übrigens empfehlen wir, die offizielleRxJS-API-Dokumentation für jeden Operator und jede Funktion, die in dieser Dokumentation verwendet werden, zu lesen, da Sie sich in jedem Projekt, das aus React mit RxJS besteht, auf einen Großteil davon verlassen werden. Beachten Sie, dass alle RxJS-Variablen mit einem Dollarzeichen ($) enden. Dies ist Teil einer Konvention für alle Observables, die ursprünglich von Angular definiert wurde, damit es einfacher ist, sie visuell vom Rest des Codes zu trennen.
Die Komponente StatusIndicator nimmt observable$ auf, wie oben im finalen Rendering zu sehen ist, und ihr Zweck ist es, das Observable zu abonnieren und die React setState Action zu übergeben, so dass die ausgegebenen Werte durch den Status der React Komponente verarbeitet werden.
{% c-block language="js" %}
import { useEffect, useState } from "react";
export const StatusIndicator = ({ observable$ }) => {
const [alarmIndicatorState, setAlarmIndicatorState] = useState();
useEffect(() => {
const sub = observable$.subscribe(setAlarmIndicatorState);
return () => sub.unsubscribe();
}, [observable$]);
return <div className="display">{alarmIndicatorState}</div>;
};
{% c-block-end %}
Schließlich nimmt die Komponente Actions das Observable actions$ auf, so dass die Schaltflächenklicks die richtige Aktion - Snooze oder Dismiss - auslösen können.
{% c-block language="js" %}
export const Actions = ({ actions$ }) => {
return (
<section className="actions">
<button className="snooze" onClick={() => actions$.next("snooze")}>
Snooze
</button>
<button className="dismiss" onClick={() => actions$.next("dismiss")}>
Dismiss
</button>
</section>
);
};
{% c-block-end %}
Dieses Beispiel basiert auf einem Konferenzvortrag überVueRx, einer Adaption von RxJS für Vue, und ist öffentlich auf CodeSandbox verfügbar.
Observable-basierte Bibliotheken können anfangs wirklich schwer zu verstehen sein, aber nachdem Sie den Beispielen gefolgt sind, die Dokumentation gründlich gelesen haben und die Best Practices der Frontend-Webentwickler-Community (sowie diesen Artikel) befolgt haben, hoffen wir, dass Sie React mit RxJS jetzt viel besser verstehen (und daher natürlich auch beherrschen) können! Viel Spaß beim Coden!