Ich komme aus der eckigen Welt, in der ich Logik zu einem Service / einer Fabrik extrahieren und sie in meinen Controllern verbrauchen könnte.
Ich versuche zu verstehen, wie ich dasselbe in einer React-Anwendung erreichen kann.
Angenommen, ich habe eine Komponente, die die Passworteingabe des Benutzers überprüft (deren Stärke). Die Logik ist ziemlich komplex, daher möchte ich sie nicht in die Komponente selbst schreiben.
Wo soll ich diese Logik schreiben? In einem Geschäft, wenn ich Flussmittel verwende? Oder gibt es eine bessere Option?
reactjs
reactjs-flux
Dennis Nerush
quelle
quelle
Antworten:
Die erste Antwort spiegelt nicht das aktuelle Paradigma von Container vs Presenter wider .
Wenn Sie etwas tun müssen, z. B. ein Passwort validieren, haben Sie wahrscheinlich eine Funktion, die dies tut. Sie würden diese Funktion an Ihre wiederverwendbare Ansicht als Requisite übergeben.
Behälter
Der richtige Weg, dies zu tun, besteht darin, einen ValidatorContainer zu schreiben, der diese Funktion als Eigenschaft hat, und das Formular darin zu verpacken und die richtigen Requisiten an das Kind zu übergeben. Wenn es um Ihre Ansicht geht, schließt Ihr Validator-Container Ihre Ansicht ein und die Ansicht verbraucht die Container-Logik.
Die Validierung kann in den Eigenschaften des Containers erfolgen. Wenn Sie jedoch einen Validator eines Drittanbieters oder einen einfachen Validierungsdienst verwenden, können Sie den Dienst als Eigenschaft der Containerkomponente verwenden und in den Methoden des Containers verwenden. Ich habe dies für erholsame Komponenten getan und es funktioniert sehr gut.
Anbieter
Wenn etwas mehr Konfiguration erforderlich ist, können Sie ein Provider / Consumer-Modell verwenden. Ein Anbieter ist eine übergeordnete Komponente, die sich in der Nähe und unterhalb des obersten Anwendungsobjekts (das von Ihnen bereitgestellten) befindet und der Kontext-API einen Teil von sich selbst oder eine in der obersten Ebene konfigurierte Eigenschaft bereitstellt. Ich setze dann meine Containerelemente so, dass sie den Kontext verbrauchen.
Die Eltern-Kind-Kontextbeziehungen müssen nicht nahe beieinander liegen, nur das Kind muss auf irgendeine Weise abstammen. Redux-Speicher und der React Router funktionieren auf diese Weise. Ich habe es verwendet, um einen Root-Restful-Kontext für meine Rest-Container bereitzustellen (wenn ich keinen eigenen bereitstelle).
(Hinweis: Die Kontext-API ist in den Dokumenten als experimentell markiert, aber ich glaube nicht, dass dies mehr der Fall ist, wenn man bedenkt, was sie verwendet.)
Middleware
Ein weiterer Weg, den ich nicht ausprobiert, aber als gebraucht angesehen habe, ist die Verwendung von Middleware in Verbindung mit Redux. Sie definieren Ihr Serviceobjekt außerhalb der Anwendung oder zumindest höher als der Redux-Speicher. Während der Filialerstellung fügen Sie den Dienst in die Middleware ein, und die Middleware verarbeitet alle Aktionen, die sich auf den Dienst auswirken.
Auf diese Weise konnte ich mein restful.js-Objekt in die Middleware einfügen und meine Containermethoden durch unabhängige Aktionen ersetzen. Ich würde immer noch eine Containerkomponente benötigen, um die Aktionen für die Formularansichtsebene bereitzustellen, aber connect () und mapDispatchToProps haben mich dort behandelt.
Der neue v4-React-Router-Redux verwendet diese Methode beispielsweise, um den Status des Verlaufs zu beeinflussen.
quelle
Das Problem wird extrem einfach, wenn Sie feststellen, dass ein Angular-Dienst nur ein Objekt ist, das eine Reihe kontextunabhängiger Methoden bereitstellt. Es ist nur der Angular DI-Mechanismus, der es komplizierter aussehen lässt. Der DI ist nützlich, da er sich um das Erstellen und Verwalten von Instanzen für Sie kümmert, diese aber nicht wirklich benötigt.
Stellen Sie sich eine beliebte AJAX-Bibliothek mit dem Namen axios vor (von der Sie wahrscheinlich schon gehört haben):
Benimmt es sich nicht als Dienstleistung? Es bietet eine Reihe von Methoden, die für eine bestimmte Logik verantwortlich sind, und ist unabhängig vom Hauptcode.
In Ihrem Beispielfall ging es darum, einen isolierten Satz von Methoden zum Überprüfen Ihrer Eingaben zu erstellen (z. B. Überprüfen der Kennwortstärke). Einige schlugen vor, diese Methoden in die Komponenten zu integrieren, was für mich eindeutig ein Anti-Muster ist. Was ist, wenn bei der Validierung XHR-Backend-Aufrufe durchgeführt und verarbeitet oder komplexe Berechnungen durchgeführt werden? Würden Sie diese Logik mit Mausklick-Handlern und anderen UI-spezifischen Dingen mischen? Unsinn. Gleiches gilt für den Container / HOC-Ansatz. Umschließen Sie Ihre Komponente nur, um eine Methode hinzuzufügen, mit der überprüft wird, ob der Wert eine Ziffer enthält? Komm schon.
Ich würde einfach eine neue Datei mit dem Namen "ValidationService.js" erstellen und sie wie folgt organisieren:
Dann in Ihrer Komponente:
Nutzen Sie diesen Service von jedem beliebigen Ort aus. Wenn sich die Validierungsregeln ändern, müssen Sie sich nur auf die Datei ValidationService.js konzentrieren.
Möglicherweise benötigen Sie einen komplizierteren Dienst, der von anderen Diensten abhängt. In diesem Fall gibt Ihre Servicedatei möglicherweise einen Klassenkonstruktor anstelle eines statischen Objekts zurück, sodass Sie selbst eine Instanz des Objekts in der Komponente erstellen können. Sie können auch ein einfaches Singleton implementieren, um sicherzustellen, dass in der gesamten Anwendung immer nur eine Instanz des Serviceobjekts verwendet wird.
quelle
Ich brauchte eine Formatierungslogik, die von mehreren Komponenten gemeinsam genutzt werden konnte, und als Angular-Entwickler neigte ich natürlich auch zu einem Dienst.
Ich teilte die Logik, indem ich sie in eine separate Datei legte
und importierte es dann als Modul
quelle
Denken Sie daran, dass der Zweck von React darin besteht, Dinge besser zu koppeln, die logisch gekoppelt werden sollten. Wenn Sie eine komplizierte "Passwort validieren" -Methode entwerfen, wo sollte sie gekoppelt werden?
Nun, Sie müssen es jedes Mal verwenden, wenn der Benutzer ein neues Passwort eingeben muss. Dies kann auf dem Registrierungsbildschirm, einem Bildschirm "Passwort vergessen", einem Administratorbildschirm "Passwort für einen anderen Benutzer zurücksetzen" usw. angezeigt werden.
In jedem dieser Fälle ist es jedoch immer an ein Texteingabefeld gebunden. Dort sollte es also gekoppelt werden.
Erstellen Sie eine sehr kleine React-Komponente, die ausschließlich aus einem Eingabefeld und der zugehörigen Validierungslogik besteht. Geben Sie diese Komponente in alle Formulare ein, für die möglicherweise eine Kennworteingabe erforderlich ist.
Es ist im Wesentlichen das gleiche Ergebnis wie ein Service / eine Factory für die Logik, aber Sie koppeln sie direkt an den Eingang. Sie müssen dieser Funktion jetzt nie mehr mitteilen, wo nach der Validierungseingabe gesucht werden soll, da sie permanent miteinander verbunden ist.
quelle
Ich kam auch aus dem Bereich Angular.js und die Dienstleistungen und Fabriken in React.js sind einfacher.
Sie können einfache Funktionen oder Klassen, Rückrufstil und Ereignis Mobx wie ich verwenden :)
Hier ist ein einfaches Beispiel:
quelle
Gleiche Situation: Nachdem Sie mehrere Angular-Projekte durchgeführt und zu React gewechselt sind, scheint es ein fehlendes Teil zu sein (abgesehen von den Einzelheiten des Dienstes), keine einfache Möglichkeit zu haben, Dienste über DI bereitzustellen.
Mit Kontext- und ES7-Dekoratoren können wir näher kommen:
https://jaysoo.ca/2015/06/09/react-contexts-and-dependency-injection/
Diese Jungs scheinen einen Schritt weiter gegangen zu sein / in eine andere Richtung:
http://blog.wolksoftware.com/dependency-injection-in-react-powered-inversifyjs
Fühlt sich immer noch an, als würde man gegen den Strich arbeiten. Wird diese Antwort in 6 Monaten nach Durchführung eines großen React-Projekts erneut prüfen.
EDIT: Zurück 6 Monate später mit etwas mehr React Erfahrung. Betrachten Sie die Art der Logik:
Einige greifen auch nach HOCs zur Wiederverwendung, aber für mich deckt das oben Gesagte fast alle Anwendungsfälle ab. Ziehen Sie auch in Betracht, die Statusverwaltung mithilfe von Enten zu skalieren , um Bedenken getrennt zu halten und die Benutzeroberfläche auf den Status auszurichten.
quelle
Ich komme ebenfalls aus Angular und probiere React aus. Ab sofort scheint eine empfohlene (?) Methode die Verwendung von Komponenten höherer Ordnung zu sein :
Angenommen, Sie haben
input
undtextarea
möchten dieselbe Validierungslogik anwenden:Schreiben Sie dann ein HOC, das die umschlossene Komponente validiert und formatiert:
Jetzt haben diese HOCs dasselbe Validierungsverhalten:
Ich habe eine einfache Demo erstellt .
Bearbeiten : Eine andere Demo verwendet Requisiten, um ein Array von Funktionen zu übergeben, sodass Sie die Logik, die aus mehreren Validierungsfunktionen besteht, über
HOC
s wie folgt gemeinsam nutzen können :Edit2 : React 16.8+ bietet eine neue Funktion, Hook , eine weitere gute Möglichkeit, Logik zu teilen.
https://stackblitz.com/edit/react-shared-validation-logic-using-hook?file=index.js
quelle
HOC
meiner Bearbeitung.Der Service ist nicht auf Angular beschränkt, auch nicht in Angular2 + .
Service ist nur eine Sammlung von Hilfsfunktionen ...
Und es gibt viele Möglichkeiten, sie zu erstellen und in der gesamten Anwendung wiederzuverwenden ...
1) Sie können alle getrennte Funktionen sein, die aus einer js-Datei exportiert werden, ähnlich wie unten:
2) Wir können auch eine Factory-Methode wie die Sammlung von Funktionen verwenden. Mit ES6 kann es sich eher um eine Klasse als um einen Funktionskonstruktor handeln:
In diesem Fall müssen Sie eine Instanz mit neuem Schlüssel erstellen ...
Auch in diesem Fall hat jede Instanz ihr eigenes Leben. Seien Sie also vorsichtig, wenn Sie es gemeinsam nutzen möchten. In diesem Fall sollten Sie nur die gewünschte Instanz exportieren ...
3) Wenn Ihre Funktion und Dienstprogramme nicht gemeinsam genutzt werden, können Sie sie sogar in die React-Komponente einfügen, in diesem Fall genauso wie die Funktion in Ihrer React-Komponente ...
4) Eine andere Möglichkeit, mit Dingen umzugehen , könnte die Verwendung von Redux sein . Es ist ein temporärer Speicher für Sie. Wenn Sie ihn also in Ihrer React-Anwendung haben , kann er Ihnen bei vielen Getter-Setter-Funktionen helfen, die Sie verwenden ... Es ist wie ein großer Speicher Das behält den Überblick über Ihre Zustände und kann diese auf Ihre Komponenten verteilen. So können Sie viele Schmerzen für Getter-Setter-Dinge, die wir in den Diensten verwenden, loswerden ...
Es ist immer gut, einen DRY-Code zu erstellen und nicht zu wiederholen, was verwendet werden muss, um den Code wiederverwendbar und lesbar zu machen. Versuchen Sie jedoch nicht, in der React-App , wie in Punkt 4 erwähnt , den Angular-Methoden zu folgen Dienste und Sie beschränken ihre Verwendung für einige wiederverwendbare Hilfsfunktionen wie Punkt 1 ...
quelle
Ich bin im selben Stiefel wie du. In dem von Ihnen erwähnten Fall würde ich die UI-Komponente für die Eingabevalidierung als React-Komponente implementieren.
Ich bin damit einverstanden, dass die Implementierung der Validierungslogik selbst nicht gekoppelt werden sollte (darf). Daher würde ich es in ein separates JS-Modul einfügen.
Das heißt, für Logik, die nicht gekoppelt werden soll, verwenden Sie ein JS-Modul / eine JS-Klasse in einer separaten Datei und verwenden Sie require / import, um die Komponente vom "Dienst" zu entkoppeln.
Dies ermöglicht die unabhängige Abhängigkeitsinjektion und das Testen von Einheiten der beiden.
quelle
oder Sie können die Klassenvererbung "http" in React Component einfügen
über Requisiten Objekt.
Update:
Bearbeiten Sie React Component ReactApp einfach wie folgt:
quelle
Nun, das am häufigsten verwendete Muster für wiederverwendbare Logik ist das Schreiben eines Hooks oder das Erstellen einer Utils-Datei. Es hängt davon ab, was Sie erreichen möchten.
Wenn Sie beispielsweise Formulardaten validieren möchten, würde ich einen benutzerdefinierten Hook mit dem Namen useForm.js erstellen und Formulardaten bereitstellen. Im Gegenzug würde ich ein Objekt zurückgeben, das zwei Dinge enthält:
Sie können definitiv mehr Dinge zurückgeben, wenn Sie Fortschritte machen.
Ein anderes Beispiel wäre, als ob Sie einige Informationen aus einer URL extrahieren möchten. Dann würde ich eine Utils-Datei dafür erstellen, die eine Funktion enthält, und sie bei Bedarf importieren:
quelle