Es gibt mehrere Möglichkeiten, Komponenten zur Kommunikation zu bringen. Einige können für Ihren Anwendungsfall geeignet sein. Hier ist eine Liste von einigen, die ich als nützlich erachtet habe.
Reagieren
Direkte Kommunikation zwischen Eltern und Kind
const Child = ({fromChildToParentCallback}) => (
<div onClick={() => fromChildToParentCallback(42)}>
Click me
</div>
);
class Parent extends React.Component {
receiveChildValue = (value) => {
console.log("Parent received value from child: " + value); // value is 42
};
render() {
return (
<Child fromChildToParentCallback={this.receiveChildValue}/>
)
}
}
Hier ruft die untergeordnete Komponente einen vom übergeordneten Element bereitgestellten Rückruf mit einem Wert auf, und das übergeordnete Element kann den von den untergeordneten Elementen im übergeordneten Element bereitgestellten Wert abrufen.
Wenn Sie eine Funktion / Seite Ihrer App erstellen, ist es besser, wenn ein einzelner Elternteil die Rückrufe / den Status (auch containeroder genannt smart component) verwaltet und alle Kinder zustandslos sind und nur dem Elternteil Bericht erstatten. Auf diese Weise können Sie den Status des Elternteils problemlos an jedes Kind weitergeben, das ihn benötigt.
Kontext
Mit React Context können Sie den Status an der Wurzel Ihrer Komponentenhierarchie halten und diesen Status problemlos in sehr tief verschachtelte Komponenten einfügen, ohne dass Sie Requisiten an alle Zwischenkomponenten weitergeben müssen.
Bisher war der Kontext eine experimentelle Funktion, aber in React 16.3 ist eine neue API verfügbar.
const AppContext = React.createContext(null)
class App extends React.Component {
render() {
return (
<AppContext.Provider value={{language: "en",userId: 42}}>
<div>
...
<SomeDeeplyNestedComponent/>
...
</div>
</AppContext.Provider>
)
}
};
const SomeDeeplyNestedComponent = () => (
<AppContext.Consumer>
{({language}) => <div>App language is currently {language}</div>}
</AppContext.Consumer>
);
Der Verbraucher nutzt die render prop / children
Überprüfen Sie diesen Blog-Beitrag für weitere Details.
Vor React 16.3 würde ich die Verwendung von React -Broadcast empfehlen, die eine ziemlich ähnliche API bieten, und die frühere Kontext-API verwenden.
Portale
Verwenden Sie ein Portal, wenn Sie zwei Komponenten nahe beieinander halten möchten, damit sie mit einfachen Funktionen kommunizieren können, wie dies bei normalen Eltern / Kindern der Fall ist. Sie möchten jedoch nicht, dass diese beiden Komponenten eine Eltern / Kind-Beziehung im DOM haben, da der damit verbundenen visuellen / CSS-Einschränkungen (wie Z-Index, Deckkraft ...).
In diesem Fall können Sie ein "Portal" verwenden. Es gibt verschiedene Reaktionsbibliotheken mit Portalen , die normalerweise für Modale , Popups, Tooltips ... verwendet werden.
Folgendes berücksichtigen:
<div className="a">
a content
<Portal target="body">
<div className="b">
b content
</div>
</Portal>
</div>
Könnte beim Rendern im Inneren das folgende DOM erzeugen reactAppContainer:
<body>
<div id="reactAppContainer">
<div className="a">
a content
</div>
</div>
<div className="b">
b content
</div>
</body>
Weitere Details hier
Schlüssel
Sie definieren irgendwo einen Slot und füllen den Slot dann an einer anderen Stelle Ihres Renderbaums.
import { Slot, Fill } from 'react-slot-fill';
const Toolbar = (props) =>
<div>
<Slot name="ToolbarContent" />
</div>
export default Toolbar;
export const FillToolbar = ({children}) =>
<Fill name="ToolbarContent">
{children}
</Fill>
Dies ist Portalen etwas ähnlich, außer dass der gefüllte Inhalt in einem von Ihnen definierten Slot gerendert wird, während Portale im Allgemeinen einen neuen Dom-Knoten rendern (häufig untergeordnete Elemente von document.body).
Überprüfen Sie die React-Slot-Fill- Bibliothek
Ereignisbus
Wie in der React- Dokumentation angegeben :
Für die Kommunikation zwischen zwei Komponenten, die keine Eltern-Kind-Beziehung haben, können Sie Ihr eigenes globales Ereignissystem einrichten. Abonnieren Sie Ereignisse in componentDidMount (), melden Sie sich in componentWillUnmount () ab und rufen Sie setState () auf, wenn Sie ein Ereignis erhalten.
Es gibt viele Dinge, mit denen Sie einen Ereignisbus einrichten können. Sie können einfach ein Array von Listenern erstellen. Bei der Veröffentlichung eines Ereignisses erhalten alle Listener das Ereignis. Oder Sie können etwas wie EventEmitter oder PostalJs verwenden
Fluss
Flux ist im Grunde ein Ereignisbus, außer dass die Ereignisempfänger Geschäfte sind. Dies ähnelt dem grundlegenden Ereignisbussystem, außer dass der Status außerhalb von React verwaltet wird
Die ursprüngliche Flux-Implementierung scheint ein Versuch zu sein, Event-Sourcing auf hackige Weise durchzuführen.
Redux ist für mich die Flux-Implementierung, die der Event-Beschaffung am nächsten kommt. Sie bietet viele Vorteile der Event-Beschaffung, z. B. die Möglichkeit, Zeitreisen zu unternehmen. Es ist nicht eng mit React verknüpft und kann auch mit anderen funktionalen Ansichtsbibliotheken verwendet werden.
Das Redux- Video-Tutorial von Egghead ist wirklich nett und erklärt, wie es intern funktioniert (es ist wirklich einfach).
Cursor
Cursor kommen aus ClojureScript / Om und werden häufig in React-Projekten verwendet. Sie ermöglichen die Verwaltung des Status außerhalb von React und ermöglichen mehreren Komponenten Lese- / Schreibzugriff auf denselben Teil des Status, ohne dass Sie etwas über den Komponentenbaum wissen müssen.
Es gibt viele Implementierungen, einschließlich ImmutableJS , React-Cursors und Omniscient
Edit 2016 : Es scheint, dass die Leute der Meinung sind, dass Cursor für kleinere Apps gut funktionieren, aber für komplexe Apps nicht gut skalierbar sind. Om Next hat keine Cursor mehr (während Om das Konzept ursprünglich eingeführt hat)
Ulmenarchitektur
Die Elm-Architektur ist eine Architektur, die von der Elm-Sprache verwendet werden soll . Auch wenn Elm nicht ReactJS ist, kann die Elm-Architektur auch in React ausgeführt werden.
Dan Abramov, der Autor von Redux, hat eine Implementierung der Elm-Architektur mit React durchgeführt.
Sowohl Redux als auch Elm sind wirklich großartig und tendieren dazu, Event-Sourcing-Konzepte im Frontend zu unterstützen. Beide ermöglichen das Debuggen von Zeitreisen, das Rückgängigmachen / Wiederherstellen, die Wiedergabe ...
Der Hauptunterschied zwischen Redux und Elm besteht darin, dass Elm in Bezug auf das staatliche Management tendenziell viel strenger ist. In Elm können keine lokalen Komponentenstatus- oder Mount / Unmount-Hooks vorhanden sein, und alle DOM-Änderungen müssen durch globale Statusänderungen ausgelöst werden. Die Elm-Architektur schlägt einen skalierbaren Ansatz vor, mit dem der gesamte Status innerhalb eines einzelnen unveränderlichen Objekts verarbeitet werden kann, während Redux einen Ansatz vorschlägt, mit dem Sie aufgefordert werden, den größten Teil des Status in einem einzelnen unveränderlichen Objekt zu verarbeiten.
Während das konzeptionelle Modell von Elm sehr elegant ist und die Architektur eine gute Skalierung auf großen Apps ermöglicht, kann es in der Praxis schwierig sein oder mehr Boilerplate erfordern, um einfache Aufgaben wie das Fokussieren auf eine Eingabe nach dem Mounten oder das Integrieren in eine vorhandene Bibliothek zu erledigen mit einer zwingenden Schnittstelle (dh JQuery-Plugin). Verwandte Ausgabe .
Außerdem beinhaltet die Elm-Architektur mehr Code-Boilerplate. Es ist nicht so ausführlich oder kompliziert zu schreiben, aber ich denke, die Elm-Architektur eignet sich eher für statisch typisierte Sprachen.
FRP
Bibliotheken wie RxJS, BaconJS oder Kefir können verwendet werden, um FRP-Streams für die Kommunikation zwischen Komponenten zu erstellen.
Sie können zum Beispiel Rx-React ausprobieren
Ich denke, die Verwendung dieser Bibliotheken ähnelt der Verwendung der ELM-Sprache mit Signalen .
Das CycleJS- Framework verwendet nicht ReactJS, sondern vdom . Es hat viele Ähnlichkeiten mit der Elm-Architektur (ist aber im wirklichen Leben einfacher zu verwenden, da es Vdom-Hooks zulässt) und verwendet RxJs häufig anstelle von Funktionen. Es kann eine gute Inspirationsquelle sein, wenn Sie FRP verwenden möchten Reagieren. CycleJs Egghead-Videos sind gut zu verstehen, wie es funktioniert.
CSP
CSP (Communicating Sequential Processes) sind derzeit beliebt (hauptsächlich wegen Go / goroutines und core.async / ClojureScript), aber Sie können sie auch in Javascript mit JS-CSP verwenden .
James Long hat ein Video gemacht, in dem erklärt wird, wie es mit React verwendet werden kann.
Sagas
Eine Saga ist ein Backend-Konzept, das aus der DDD / EventSourcing / CQRS-Welt stammt und auch als "Prozessmanager" bezeichnet wird. Es wird vom Redux-Saga- Projekt populär gemacht , hauptsächlich als Ersatz für Redux-Thunk zur Behandlung von Nebenwirkungen (dh API-Aufrufe usw.). Die meisten Leute denken derzeit, dass es nur um Nebenwirkungen geht, aber es geht eigentlich mehr darum, Komponenten zu entkoppeln.
Es ist eher ein Kompliment an eine Flux-Architektur (oder Redux) als an ein völlig neues Kommunikationssystem, da die Saga am Ende Flux-Aktionen ausgibt. Die Idee ist, dass Sie, wenn Sie Widget1 und Widget2 haben und möchten, dass sie entkoppelt werden, keine Aktion auslösen können, die auf Widget2 von Widget1 abzielt. Sie lassen also Widget1 nur Aktionen abfeuern, die auf sich selbst abzielen, und die Saga ist ein "Hintergrundprozess", der auf Widget1-Aktionen wartet und möglicherweise Aktionen auslöst, die auf Widget2 abzielen. Die Saga ist der Kopplungspunkt zwischen den beiden Widgets, aber die Widgets bleiben entkoppelt.
Wenn Sie interessiert sind, schauen Sie sich meine Antwort hier an
Fazit
Wenn Sie ein Beispiel derselben kleinen App mit diesen verschiedenen Stilen sehen möchten, überprüfen Sie die Zweige dieses Repositorys .
Ich weiß nicht, was auf lange Sicht die beste Option ist, aber ich mag es wirklich, wie Flux wie Event-Sourcing aussieht.
Wenn Sie keine Event-Sourcing-Konzepte kennen, werfen Sie einen Blick auf diesen sehr pädagogischen Blog: Wenn Sie die Datenbank mit Apache Samza auf den Kopf stellen , ist es ein Muss zu verstehen, warum Flux nett ist (dies könnte jedoch auch für FRP gelten )
Ich denke, die Community ist sich einig, dass die vielversprechendste Flux-Implementierung Redux ist , die dank Hot-Reloading nach und nach eine sehr produktive Entwicklererfahrung ermöglicht. Beeindruckende Live-Codierung von Bret Victors Video " Inventing on Principle" ist möglich!