ReactJS ist zu einer Standardbibliothek für den Aufbau dynamischer und reaktionsfähiger Benutzeroberflächen geworden. Jedoch wird es mit zunehmender Größe von Anwendungen schwieriger, asynchrone Datenströme zu verwalten. Hier kommt RxJS ins Spiel, eine leistungsstarke Bibliothek für reaktive Programmierung mit Observables. RxJS-Operatoren vereinfachen die Handhabung komplexer asynchroner Datenflüsse und machen Ihre React-Komponenten leichter verwaltbar und effizienter.
In diesem Artikel werden wir RxJS-Operatoren im Kontext von ReactJS erkunden. Wir werden schrittweise Beispiele durchgehen, die zeigen, wie Sie RxJS in Ihre React-Anwendungen integrieren können. Am Ende dieses Leitfadens werden Sie ein solides Verständnis von RxJS-Operatoren haben und wie sie Ihre ReactJS-Projekte verbessern können.
Was ist RxJS?
RxJS, oder Reactive Extensions für JavaScript, ist eine Bibliothek, die es Ihnen ermöglicht, mit asynchronen Datenströmen mithilfe von Observables zu arbeiten. Ein Observable ist eine Sammlung, die im Laufe der Zeit eintrifft und es Ihnen ermöglicht, effizient auf Änderungen in den Daten zu reagieren.
Aber warum RxJS in ReactJS verwenden? ReactJS ist von Natur aus zustandsbehaftet und beschäftigt sich mit der UI-Renderung. Die Integration von RxJS ermöglicht es Ihnen, komplexe asynchrone Operationen wie API-Aufrufe, Ereignisbehandlungen und Zustandsverwaltung mit größerer Leichtigkeit und Vorhersagbarkeit zu handhaben.
Warum sollten Sie RxJS in ReactJS verwenden?
Verbesserte Handhabung von asynchronen Vorgängen
In ReactJS kann die Handhabung asynchroner Operationen wie API-Aufrufe oder Benutzerereignisse umständlich werden. RxJS-Operatoren wie map, filter und debounceTime ermöglichen es Ihnen, diese Vorgänge elegant zu verwalten, indem sie Datenströme transformieren, während sie durch Ihre Anwendung fließen.
Sauberer und Lesbarer Code
RxJS fördert einen funktionalen Programmieransatz, der Ihren Code deklarativer macht. Anstatt Zustandsänderungen und Nebenwirkungen manuell zu verwalten, können Sie RxJS-Operatoren nutzen, um diese Aufgaben prägnant zu erledigen.
Verbesserte Fehlerbehandlung
RxJS bietet leistungsstarke Mechanismen zur Fehlerbehandlung, die es Ihnen ermöglichen, Fehler in Ihren asynchronen Operationen elegant zu verwalten. Operatoren wie catchError und retry können automatisch von Fehlern wiederherstellen, ohne Ihren Code mit try-catch-Blöcken zu überladen.
RxJS in einem ReactJS-Projekt Einrichten
Bevor wir in den Code eintauchen, lassen Sie uns ein grundlegendes ReactJS-Projekt mit installiertem RxJS einrichten.
npx create-react-app rxjs-react-example
cd rxjs-react-example
npm install rxjs
Sobald Sie RxJS installiert haben, sind Sie bereit, es in Ihre React-Komponenten zu integrieren.
Schritt-für-Schritt-Beispiel
Lassen Sie uns ein detailliertes Beispiel für die Verwendung von RxJS in einer ReactJS-Anwendung durchgehen. Wir erstellen eine einfache App, die Daten von einer API abruft und sie in einer Liste anzeigt. Wir verwenden RxJS-Operatoren, um den asynchronen Datenstrom effizient zu verwalten.
Schritt 1: Erstellen einer einfachen React-Komponente
Zuerst erstellen Sie eine neue Komponente namens DataFetcher.js
:
import React, { useEffect, useState } from 'react';
const DataFetcher = () => {
const [data, setData] = useState([]);
const [error, setError] = useState(null);
return (
<div>
<h1>Data Fetcher</h1>
{error && <p>Error: {error}</p>}
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
};
export default DataFetcher;
Diese Komponente initialisiert Zustandsvariablen für Daten und Fehler. Sie rendert eine Liste von Daten, die von einer API abgerufen wurden, und behandelt Fehler elegant.
Schritt 2: Importieren von RxJS und Erstellen eines Observables
Als Nächstes importieren wir RxJS und erstellen ein Observable zum Abrufen von Daten. Ändern Sie im gleichen DataFetcher.js
-Datei die Komponente, um Folgendes einzuschließen:
import { of, from } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { ajax } from 'rxjs/ajax';
const fetchData = () => {
return ajax.getJSON('https://jsonplaceholder.typicode.com/users').pipe(
map(response => response),
catchError(error => of({ error: true, message: error.message }))
);
};
Hier verwenden wir die ajax.getJSON
-Methode von RxJS, um Daten von einer API abzurufen. Der map-Operator transformiert die Antwort, und catchError behandelt Fehler und gibt ein Observable zurück, dem wir uns anschließen können.
Schritt 3: Abonnieren des Observables in useEffect
Jetzt verwenden wir den useEffect
-Hook, um das Observable zu abonnieren und den Komponentenzustand entsprechend zu aktualisieren:
useEffect(() => {
const subscription = fetchData().subscribe({
next: (result) => {
if (result.error) {
setError(result.message);
} else {
setData(result);
}
},
error: (err) => setError(err.message),
});
return () => subscription.unsubscribe();
}, []);
Dieser Code abonniert das fetchData
-Observable. Wenn das Observable einen Fehler emittiert, wird der Fehlerzustand aktualisiert; andernfalls wird der Datenzustand aktualisiert. Das Abonnement wird bereinigt, wenn die Komponente ausgehängt wird, um Speicherlecks zu vermeiden.
Schritt 4: Verbesserung des Datenabrufprozesses
Jetzt, da wir eine grundlegende Implementierung haben, verbessern wir sie mit weiteren RxJS-Operatoren. Zum Beispiel können wir einen Ladezustand hinzufügen und die API-Aufrufe verzögern, um die Leistung zu optimieren.
import {
debounceTime,
tap
} from 'rxjs/operators';
const fetchData = () => {
return ajax.getJSON('https://jsonplaceholder.typicode.com/users').pipe(
debounceTime(500),
tap(() => setLoading(true)),
map(response => response),
catchError(error => of({
error: true,
message: error.message
})),
tap(() => setLoading(false))
);
};
In dieser verbesserten Version stellt debounceTime
sicher, dass der API-Aufruf nur nach 500ms Inaktivität erfolgt, was unnötige Anfragen reduziert. Der tap-Operator setzt den Ladezustand vor und nach dem API-Aufruf, um dem Benutzer eine visuelle Rückmeldung zu geben.
Übliche RxJS-Operatoren und ihre Verwendung in ReactJS
RxJS bietet eine Vielzahl von Operatoren, die in ReactJS-Anwendungen unglaublich nützlich sein können. Hier sind einige gängige Operatoren und wie sie verwendet werden können:
map
Der map
-Operator transformiert jeden Wert, der von einem Observable emittiert wird. In ReactJS kann er verwendet werden, um Daten vor dem Rendern im UI zu formatieren.
const transformedData$ = fetchData().pipe(
map(data => data.map(item => ({ item, fullName: `${item.name} (${item.username})` })))
);
filter
Der filter
-Operator ermöglicht es Ihnen, Werte herauszufiltern, die bestimmte Kriterien nicht erfüllen. Dies ist nützlich, um nur relevante Daten für den Benutzer anzuzeigen.
const filteredData$ = fetchData().pipe(
filter(item => item.isActive)
);
debounceTime
debounceTime
verzögert die Emission von Werten aus einem Observable und eignet sich daher ideal für die Handhabung von Benutzereingabeereignissen wie Suchanfragen.
const searchInput$ = fromEvent(searchInput, 'input').pipe(
debounceTime(300),
map(event => event.target.value);
);
switchMap
switchMap
ist perfekt für Szenarien, in denen nur das neueste Ergebnis eines Observables wichtig ist, wie z. B. bei automatischen Vorschlägen.
const autocomplete$ = searchInput$.pipe(
switchMap(query => ajax.getJSON(`/api/search?q=${query}`))
);
Fortgeschrittene RxJS- und ReactJS-Integration: Nutzung weiterer Operatoren und Muster
Kombination von Observables mit merge
Manchmal müssen Sie mehrere asynchrone Streams gleichzeitig verarbeiten. Der merge
-Operator ermöglicht es Ihnen, mehrere Observables in ein einziges Observable zu kombinieren und Werte aus jedem zu emittieren, sobald sie eintreffen.
import {
merge,
of,
interval
} from 'rxjs';
import {
map
} from 'rxjs/operators';
const observable1 = interval(1000).pipe(map(val => `Stream 1: ${val}`));
const observable2 = interval(1500).pipe(map(val => `Stream 2: ${val}`));
const combined$ = merge(observable1, observable2);
useEffect(() => {
const subscription = combined$.subscribe(value => {
console.log(value); // Logs values from both streams as they arrive
});
return () => subscription.unsubscribe();
}, []);
In einer React-App können Sie merge verwenden, um gleichzeitig mehreren Ereignissen oder API-Aufrufen zuzuhören und sie auf einheitliche Weise zu verarbeiten.
Echtzeit-Datenströme mit interval und scan
Für Anwendungen, die Echtzeit-Updates erfordern, wie z. B. Aktienkurse oder Live-Dashboards, kann RxJS Streams effektiv erstellen und verarbeiten.
import { interval } from 'rxjs';
import { scan } from 'rxjs/operators';
const ticker$ = interval(1000).pipe(
scan(count => count + 1, 0)
);
useEffect(() => {
const subscription = ticker$.subscribe(count => {
console.log(`Tick: ${count}`); // Logs ticks every second
});
return () => subscription.unsubscribe();
}, []);
In diesem Beispiel fungiert scan
wie ein Reduzierer, der einen kumulativen Zustand über Emissionen hinweg beibehält.
Fortgeschrittene Handhabung von Benutzereingaben mit combineLatest
Für komplexe Formulare oder Szenarien, in denen mehrere Eingabefelder interagieren, ist der combineLatest
-Operator von unschätzbarem Wert.
import {
fromEvent,
combineLatest
} from 'rxjs';
import {
map
} from 'rxjs/operators';
const emailInput = document.getElementById('email');
const passwordInput = document.getElementById('password');
const email$ = fromEvent(emailInput, 'input').pipe(
map(event => event.target.value)
);
const password$ = fromEvent(passwordInput, 'input').pipe(
map(event => event.target.value)
);
const form$ = combineLatest([email$, password$]).pipe(
map(([email, password]) => ({
email,
password
}))
);
useEffect(() => {
const subscription = form$.subscribe(formData => {
console.log('Form Data:', formData);
});
return () => subscription.unsubscribe();
}, []);
Dieses Beispiel hört auf mehrere Eingabefelder und gibt die neuesten Werte zusammen aus, was die Verwaltung des Formularzustands vereinfacht.
Wiederholungslogik mit retryWhen und delay
In Szenarien, in denen die Netzwerkzuverlässigkeit ein Problem darstellt, kann RxJS helfen, Wiederholungsmechanismen mit exponentiellem Backoff zu implementieren.
import {
ajax
} from 'rxjs/ajax';
import {
retryWhen,
delay,
scan
} from 'rxjs/operators';
const fetchData = () => {
return ajax.getJSON('https://api.example.com/data').pipe(
retryWhen(errors =>
errors.pipe(
scan((retryCount, err) => {
if (retryCount >= 3) throw err;
return retryCount + 1;
}, 0),
delay(2000)
)
)
);
};
useEffect(() => {
const subscription = fetchData().subscribe({
next: data => setData(data),
error: err => setError(err.message)
});
return () => subscription.unsubscribe();
}, []);
Dieser Ansatz wiederholt den API-Aufruf bis zu drei Mal mit einer Verzögerung zwischen den Versuchen und verbessert so die Benutzererfahrung bei vorübergehenden Ausfällen.
Ladeanzeigen mit startWith
Um eine nahtlose Benutzererfahrung zu bieten, können Sie einen Ladeindikator anzeigen, bis Daten verfügbar sind, indem Sie den startWith
-Operator verwenden.
import {
ajax
} from 'rxjs/ajax';
import {
startWith
} from 'rxjs/operators';
const fetchData = () => {
return ajax.getJSON('https://jsonplaceholder.typicode.com/users').pipe(
startWith([]) // Emit an empty array initially
);
};
useEffect(() => {
const subscription = fetchData().subscribe(data => {
setData(data);
});
return () => subscription.unsubscribe();
}, []);
Dies stellt sicher, dass die Benutzeroberfläche einen Platzhalter oder Spinner anzeigt, bis Daten geladen sind.
Abbrechbare Anfragen mit takeUntil
Die Bereinigung von asynchronen Operationen ist entscheidend, insbesondere für Such- oder dynamische Abfragen. Der takeUntil
-Operator hilft dabei, Observables abzubrechen.
import {
Subject
} from 'rxjs';
import {
ajax
} from 'rxjs/ajax';
import {
debounceTime,
switchMap,
takeUntil
} from 'rxjs/operators';
const search$ = new Subject();
const cancel$ = new Subject();
const searchObservable = search$.pipe(
debounceTime(300),
switchMap(query =>
ajax.getJSON(`https://api.example.com/search?q=${query}`).pipe(
takeUntil(cancel$)
)
)
);
useEffect(() => {
const subscription = searchObservable.subscribe(data => {
setData(data);
});
return () => cancel$.next(); // Cancel ongoing requests on unmount
}, []);
const handleSearch = (query) => search$.next(query);
Hier stellt takeUntil sicher, dass laufende API-Aufrufe abgebrochen werden, wenn eine neue Abfrage eingegeben wird oder das Komponente entladen wird.
FAQs
Was ist der Unterschied zwischen RxJS und Redux?
RxJS konzentriert sich auf die Verwaltung asynchroner Datenströme mit Observables, während Redux eine State-Management-Bibliothek ist. RxJS kann mit Redux verwendet werden, um komplexe asynchrone Logik zu handhaben, aber sie dienen unterschiedlichen Zwecken.
Kann ich RxJS mit funktionalen Komponenten verwenden?
Ja, RxJS funktioniert nahtlos mit Reacts funktionalen Komponenten. Sie können Hooks wie useEffect
verwenden, um sich bei Observables anzumelden und Nebeneffekte zu verwalten.
Ist RxJS überdimensioniert für kleine React-Projekte?
Für kleine Projekte mag RxJS überdimensioniert erscheinen. Wenn Ihr Projekt jedoch wächst und Sie komplexe asynchrone Datenflüsse verarbeiten müssen, kann RxJS Ihren Code vereinfachen und wartbarer machen.
Wie debugge ich RxJS in ReactJS?
Das Debuggen von RxJS-Code kann mit Tools wie den Redux DevTools oder RxJS-spezifischen Logging-Operatoren wie tap
erfolgen, um emittierte Werte in verschiedenen Phasen zu inspizieren.
Wie optimiere ich für Ereignisse hoher Frequenz?
Operatoren wie throttleTime
und auditTime
eignen sich ideal für die Verarbeitung von Ereignissen hoher Frequenz wie Scrollen oder Größenänderungen.
Kann RxJS React State Management-Bibliotheken ersetzen?
RxJS ist keine State-Management-Lösung, kann jedoch Bibliotheken wie Redux zur Handhabung komplexer asynchroner Logik ergänzen. Für kleinere Projekte kann RxJS mit BehaviorSubject
manchmal State-Management-Bibliotheken ersetzen.
Was sind bewährte Praktiken für RxJS in ReactJS?
- Verwenden Sie
takeUntil
für die Bereinigung inuseEffect
, um Speicherlecks zu vermeiden. - Vermeiden Sie die übermäßige Verwendung von RxJS für einfache synchrone Statusaktualisierungen; bevorzugen Sie dafür Reacts integrierte Tools.
- Testen Sie Observables unabhängig, um die Zuverlässigkeit zu gewährleisten.
Conclusion
RxJS ist ein leistungsstarkes Werkzeug zur Verwaltung von asynchronen Daten in ReactJS-Anwendungen. Mit RxJS-Operatoren können Sie einen saubereren, effizienteren und wartbareren Code schreiben. Das Verständnis und die Anwendung von RxJS in Ihren ReactJS-Projekten wird Ihre Fähigkeit zur Bewältigung komplexer asynchroner Datenflüsse erheblich verbessern und Ihre Anwendungen skalierbarer machen.
Source:
https://dzone.com/articles/reactive-programming-in-react-with-rxjs