In diesem Teil der Übersetzung des React-Tutorials werden wir über die Architektur von React-Anwendungen sprechen. Insbesondere werden wir das beliebte Container / Component-Muster diskutieren.

→
Teil 1: Kursübersicht, Gründe für die Beliebtheit von React, ReactDOM und JSX→
Teil 2: Funktionskomponenten→
Teil 3: Komponentendateien, Projektstruktur→
Teil 4: übergeordnete und untergeordnete Komponenten→
Teil 5: Beginn der Arbeit an einer TODO-Anwendung, Grundlagen des Stylings→
Teil 6: Über einige Funktionen des Kurses, JSX und JavaScript→
Teil 7: Inline-Stile→
Teil 8: Fortsetzung der Arbeit an der TODO-Anwendung, Vertrautheit mit den Eigenschaften von Komponenten→
Teil 9: Komponenteneigenschaften→
Teil 10: Workshop zum Arbeiten mit Komponenteneigenschaften und Styling→
Teil 11: Dynamische Markup-Generierung und Map-Arrays-Methode→
Teil 12: Workshop, dritte Phase der Arbeit an einer TODO-Anwendung→
Teil 13: Klassenbasierte Komponenten→
Teil 14: Workshop zu klassenbasierten Komponenten, Komponentenstatus→
Teil 15: Komponentengesundheitsworkshops→
Teil 16: Die vierte Phase der Arbeit an einer TODO-Anwendung, die Ereignisbehandlung→
Teil 17: Fünfte Phase der Arbeit an einer TODO-Anwendung, Änderung des Status von Komponenten→
Teil 18: Die sechste Phase der Arbeit an einer TODO-Anwendung→
Teil 19: Methoden des KomponentenlebenszyklusTeil 20: Die erste Lektion in bedingtem Rendern→
Teil 21: Zweite Lektion und Workshop zum bedingten Rendern→
Teil 22: Die siebte Phase der Arbeit an einer TODO-Anwendung, bei der Daten aus externen Quellen heruntergeladen werden→
Teil 23: Erste Lektion zum Arbeiten mit Formularen→
Teil 24: Lektion der zweiten Form→
Teil 25: Workshop zum Arbeiten mit Formularen→
Teil 26: Anwendungsarchitektur, Container- / Komponentenmuster→
Teil 27: KursprojektLektion 44. Anwendungsarchitektur, Container- / Komponentenmuster
→
OriginalManchmal ist der Arbeitsaufwand, für den eine separate Komponente verantwortlich ist, zu groß, und eine Komponente muss zu viele Aufgaben lösen. Mit dem Container / Component-Muster können Sie die Logik der Anwendung von der Logik der Bildung ihrer visuellen Darstellung trennen. Auf diese Weise können Sie die Struktur der Anwendung verbessern und die Verantwortung für die Ausführung verschiedener Aufgaben zwischen verschiedenen Komponenten teilen.
In der vorherigen praktischen Lektion haben wir eine riesige Komponente erstellt, deren Codelänge sich 150 Zeilen nähert. Hier ist der Code, den wir dann bekommen haben:
import React, {Component} from "react" class App extends Component { constructor() { super() this.state = { firstName: "", lastName: "", age: "", gender: "", destination: "", isVegan: false, isKosher: false, isLactoseFree: false } this.handleChange = this.handleChange.bind(this) } handleChange(event) { const {name, value, type, checked} = event.target type === "checkbox" ? this.setState({ [name]: checked }) : this.setState({ [name]: value }) } render() { return ( <main> <form> <input name="firstName" value={this.state.firstName} onChange={this.handleChange} placeholder="First Name" /> <br /> <input name="lastName" value={this.state.lastName} onChange={this.handleChange} placeholder="Last Name" /> <br /> <input name="age" value={this.state.age} onChange={this.handleChange} placeholder="Age" /> <br /> <label> <input type="radio" name="gender" value="male" checked={this.state.gender === "male"} onChange={this.handleChange} /> Male </label> <br /> <label> <input type="radio" name="gender" value="female" checked={this.state.gender === "female"} onChange={this.handleChange} /> Female </label> <br /> <select value={this.state.destination} name="destination" onChange={this.handleChange} > <option value="">-- Please Choose a destination --</option> <option value="germany">Germany</option> <option value="norway">Norway</option> <option value="north pole">North Pole</option> <option value="south pole">South Pole</option> </select> <br /> <label> <input type="checkbox" name="isVegan" onChange={this.handleChange} checked={this.state.isVegan} /> Vegan? </label> <br /> <label> <input type="checkbox" name="isKosher" onChange={this.handleChange} checked={this.state.isKosher} /> Kosher? </label> <br /> <label> <input type="checkbox" name="isLactoseFree" onChange={this.handleChange} checked={this.state.isLactoseFree} /> Lactose Free? </label> <br /> <button>Submit</button> </form> <hr /> <h2><font color="#3AC1EF">Entered information:</font></h2> <p>Your name: {this.state.firstName} {this.state.lastName}</p> <p>Your age: {this.state.age}</p> <p>Your gender: {this.state.gender}</p> <p>Your destination: {this.state.destination}</p> <p>Your dietary restrictions:</p> <p>Vegan: {this.state.isVegan ? "Yes" : "No"}</p> <p>Kosher: {this.state.isKosher ? "Yes" : "No"}</p> <p>Lactose Free: {this.state.isLactoseFree ? "Yes" : "No"}</p> </main> ) } } export default App
Der erste Nachteil dieses Codes, der sofort ins Auge fällt, ist, dass Sie ihn bei der Arbeit ständig im Editorfenster scrollen müssen.
Sie können feststellen, dass der Großteil dieses Codes die Logik der Bildung der Anwendungsschnittstelle ist, der Inhalt der
render()
-Methode. Darüber hinaus ist eine bestimmte Menge an Code für die Initialisierung des Status der Komponente verantwortlich. Die Komponente verfügt auch über eine sogenannte „Geschäftslogik“ (dh eine Implementierung der Logik der Funktionsweise der Anwendung). Dies ist der Code der
handleChange()
-Methode.
Nach den Ergebnissen einiger Studien ist bekannt, dass die Fähigkeit eines Programmierers, den Code, den er betrachtet, wahrzunehmen, stark beeinträchtigt ist, wenn der Code lang genug ist, und der Programmierer muss das Scrollen verwenden, um ihn in seiner Gesamtheit anzuzeigen. Ich habe das während des Unterrichts bemerkt. Wenn sich herausstellt, dass der Code, über den ich spreche, ziemlich lang ist und ich ständig durch ihn scrollen muss, wird es für die Schüler schwieriger, ihn wahrzunehmen.
Es wäre schön, wenn wir unseren Code überarbeiten und die Verantwortung zwischen den verschiedenen Komponenten für die Bildung der Anwendungsschnittstelle (was jetzt in der
render()
-Methode beschrieben wird) und für die Implementierung der Anwendungslogik teilen, dh per Definition, wie sie aussehen soll Schnittstelle (der entsprechende Code wird jetzt durch den Konstruktor der Komponente, in der der Status initialisiert wird, und den
handleChange()
Steuerereignishandler dargestellt). Wenn Sie diesen Ansatz für das Anwendungsdesign verwenden, arbeiten wir tatsächlich mit zwei Arten von Komponenten, und es sollte beachtet werden, dass Sie möglicherweise unterschiedliche Namen für solche Komponenten finden.
Wir werden hier das Container / Component-Muster verwenden. Bei der Verwendung werden Anwendungen erstellt, indem die Komponenten in zwei Typen unterteilt werden - in Containerkomponenten (das Wort Container im Namen des Musters bezieht sich auf sie) und Präsentationskomponenten (dies ist Komponente im Namen des Musters). Manchmal werden Containerkomponenten als "intelligente" Komponenten oder einfach als "Container" bezeichnet, und Präsentationskomponenten werden als "dumme" Komponenten oder einfach als "Komponenten" bezeichnet. Es gibt andere Namen für diese Arten von Komponenten, und es sollte beachtet werden, dass die in diesen Namen enthaltene Bedeutung von Fall zu Fall in bestimmten Merkmalen unterschiedlich sein kann. Im Allgemeinen besteht die allgemeine Idee dieses Ansatzes darin, dass wir eine Containerkomponente haben, die für das Speichern des Status verantwortlich ist und Methoden zum Verwalten des Status enthält, und die Logik zum Bilden der Schnittstelle auf eine andere Präsentationskomponente übertragen wird. Diese Komponente ist nur für den Empfang von Eigenschaften von der Containerkomponente und für die korrekte Bildung der Schnittstelle verantwortlich.
→
Hier ist Dan Abramovs Material, in dem er diese Idee untersucht.
Wir transformieren den Code unserer Anwendung gemäß dem Container / Component-Muster.
Lassen Sie uns zunächst darauf achten, dass jetzt alles in der Anwendung in einer einzigen Komponente der
App
. Diese Anwendung ist so konzipiert, dass ihre Struktur so weit wie möglich vereinfacht wird. In realen Projekten ist die
App
Komponente jedoch kaum sinnvoll, die Aufgabe des Renderns des Formulars zu übertragen und Code darin aufzunehmen, um die Arbeit der internen Mechanismen dieses Formulars zu organisieren.
Fügen Sie dem Ordner, in dem sich die Datei
App.js
befindet, die Datei
Form.js
, in der sich der Code der neuen Komponente befindet. Wir übertragen den gesamten Code von der
App
Komponente in diese Datei und konvertieren die
App
Komponente, die jetzt durch die auf der Klasse basierende Komponente dargestellt wird, in eine Funktionskomponente, deren Hauptaufgabe die Ausgabe der
Form
Komponente ist. Vergessen Sie nicht, die
Form
in die
App
Komponente zu importieren. Infolgedessen sieht der Code der
App
Komponente folgendermaßen aus:
import React, {Component} from "react" import Form from "./Form" function App() { return ( <Form /> ) } export default App
In dieser Phase der Arbeit wird die Anwendung auf dem Bildschirm angezeigt.
Anwendung im BrowserIn früheren Kursen habe ich Ihnen gesagt, dass ich es vorziehen würde, wenn die
App
Komponente so etwas wie ein „Inhaltsverzeichnis“ der Anwendung ist, das angibt, in welcher Reihenfolge ihre Abschnitte auf der Seite angezeigt werden, dargestellt durch andere Komponenten, die delegierte Aufgaben zum Rendern großer Fragmente der Anwendung sind.
Wir haben die Struktur der Anwendung leicht verbessert, aber das Hauptproblem, das sich in der Tatsache äußert, dass eine Komponente zu viel Verantwortung trägt, wurde noch nicht gelöst. Wir haben einfach alles, was früher in der
App
Komponente war, in die
Form
Komponente übertragen. Deshalb werden wir jetzt dieses Problem lösen. Erstellen Sie dazu in demselben Ordner, in dem sich die Dateien
Form.js
und
App.js
, eine weitere Datei -
FormComponent.js
. Diese Datei stellt die Präsentationskomponente dar, die für die Visualisierung des Formulars verantwortlich ist. Tatsächlich können Sie es anders benennen, Sie können Komponentendateien unterschiedlich strukturieren, alles hängt von den Anforderungen und dem Umfang eines bestimmten Projekts ab. Die Datei
Form.js
enthält die Logik des Formulars,
Form.js
den Code der Containerkomponente.
FormContainer.js
es daher in
FormContainer.js
und ändern Sie den Importbefehl im Code der
App
Komponente, um es in dieses Formular zu bringen:
import Form from "./FormContainer"
Sie können die
Form
Komponente auch in
FormContainer
umbenennen,
FormContainer
wird jedoch nicht durchgeführt. Jetzt übertragen wir den Code, der für das Rendern des Formulars verantwortlich ist, aus der Datei
FormContainer.js
in die Datei
FormComponent.js
.
Die
FormComponent
Komponente ist funktionsfähig. So wird sein Code in dieser Phase der Arbeit aussehen:
function FormComponent(props) { return ( <main> <form> <input name="firstName" value={this.state.firstName} onChange={this.handleChange} placeholder="First Name" /> <br /> <input name="lastName" value={this.state.lastName} onChange={this.handleChange} placeholder="Last Name" /> <br /> <input name="age" value={this.state.age} onChange={this.handleChange} placeholder="Age" /> <br /> <label> <input type="radio" name="gender" value="male" checked={this.state.gender === "male"} onChange={this.handleChange} /> Male </label> <br /> <label> <input type="radio" name="gender" value="female" checked={this.state.gender === "female"} onChange={this.handleChange} /> Female </label> <br /> <select value={this.state.destination} name="destination" onChange={this.handleChange} > <option value="">-- Please Choose a destination --</option> <option value="germany">Germany</option> <option value="norway">Norway</option> <option value="north pole">North Pole</option> <option value="south pole">South Pole</option> </select> <br /> <label> <input type="checkbox" name="isVegan" onChange={this.handleChange} checked={this.state.isVegan} /> Vegan? </label> <br /> <label> <input type="checkbox" name="isKosher" onChange={this.handleChange} checked={this.state.isKosher} /> Kosher? </label> <br /> <label> <input type="checkbox" name="isLactoseFree" onChange={this.handleChange} checked={this.state.isLactoseFree} /> Lactose Free? </label> <br /> <button>Submit</button> </form> <hr /> <h2><font color="#3AC1EF">Entered information:</font></h2> <p>Your name: {this.state.firstName} {this.state.lastName}</p> <p>Your age: {this.state.age}</p> <p>Your gender: {this.state.gender}</p> <p>Your destination: {this.state.destination}</p> <p>Your dietary restrictions:</p> <p>Vegan: {this.state.isVegan ? "Yes" : "No"}</p> <p>Kosher: {this.state.isKosher ? "Yes" : "No"}</p> <p>Lactose Free: {this.state.isLactoseFree ? "Yes" : "No"}</p> </main> ) }
Wenn Sie sich diesen Code ansehen, wird klar, dass wir uns nicht darauf beschränken können, ihn einfach von Datei zu Datei zu übertragen, da jetzt Links zum Status (z. B.
this.state.firstName
) und zum Ereignishandler (
this.handleChange
) vorhanden sind. Diese befand sich früher in derselben Komponente, basierend auf der Klasse, in der sich dieser Rendering-Code befand. Jetzt wird alles, was zuvor aus derselben Klasse stammt, in der sich der Rendering-Code befand, aus den an die Komponente übergebenen Eigenschaften übernommen. Es gibt einige andere Probleme. Jetzt werden wir diesen Code
FormContainer.js
, aber zuerst kehren wir zum Code der
Form
Komponente zurück, der sich jetzt in der Datei
FormContainer.js
.
Die
render()
-Methode ist jetzt leer. Wir müssen die
FormComponent
Komponente in dieser Methode anzeigen und die Übertragung der erforderlichen Eigenschaften darauf organisieren. Wir importieren die
FormComponent
in die
Form
Datei und zeigen die
FormComponent
in der
render()
-Methode an, übergeben ihr einen Ereignishandler und als Objekt den Status. Der Code der
Form
sieht nun folgendermaßen aus:
import React, {Component} from "react" import FormComponent from "./FormComponent" class Form extends Component { constructor() { super() this.state = { firstName: "", lastName: "", age: "", gender: "", destination: "", isVegan: false, isKosher: false, isLactoseFree: false } this.handleChange = this.handleChange.bind(this) } handleChange(event) { const {name, value, type, checked} = event.target type === "checkbox" ? this.setState({ [name]: checked }) : this.setState({ [name]: value }) } render() { return( <FormComponent handleChange={this.handleChange} data={this.state} /> ) } } export default Form
Wir werden
FormComponent
Code der
FormComponent
Komponente
FormComponent
und in das folgende Formular bringen:
import React from "react" function FormComponent(props) { return ( <main> <form> <input name="firstName" value={props.data.firstName} onChange={props.handleChange} placeholder="First Name" /> <br /> <input name="lastName" value={props.data.lastName} onChange={props.handleChange} placeholder="Last Name" /> <br /> <input name="age" value={props.data.age} onChange={props.handleChange} placeholder="Age" /> <br /> <label> <input type="radio" name="gender" value="male" checked={props.data.gender === "male"} onChange={props.handleChange} /> Male </label> <br /> <label> <input type="radio" name="gender" value="female" checked={props.data.gender === "female"} onChange={props.handleChange} /> Female </label> <br /> <select value={props.data.destination} name="destination" onChange={props.handleChange} > <option value="">-- Please Choose a destination --</option> <option value="germany">Germany</option> <option value="norway">Norway</option> <option value="north pole">North Pole</option> <option value="south pole">South Pole</option> </select> <br /> <label> <input type="checkbox" name="isVegan" onChange={props.handleChange} checked={props.data.isVegan} /> Vegan? </label> <br /> <label> <input type="checkbox" name="isKosher" onChange={props.handleChange} checked={props.data.isKosher} /> Kosher? </label> <br /> <label> <input type="checkbox" name="isLactoseFree" onChange={props.handleChange} checked={props.data.isLactoseFree} /> Lactose Free? </label> <br /> <button>Submit</button> </form> <hr /> <h2><font color="#3AC1EF">Entered information:</font></h2> <p>Your name: {props.data.firstName} {props.data.lastName}</p> <p>Your age: {props.data.age}</p> <p>Your gender: {props.data.gender}</p> <p>Your destination: {props.data.destination}</p> <p>Your dietary restrictions:</p> <p>Vegan: {props.data.isVegan ? "Yes" : "No"}</p> <p>Kosher: {props.data.isKosher ? "Yes" : "No"}</p> <p>Lactose Free: {props.data.isLactoseFree ? "Yes" : "No"}</p> </main> ) } export default FormComponent
Hier haben wir den Code unter Berücksichtigung der Tatsache korrigiert, dass die Komponente jetzt Daten und eine Verknüpfung zum Ereignishandler über Eigenschaften empfängt.
Nach all diesen Transformationen wird sich weder das Erscheinungsbild des Formulars noch seine Funktionsweise ändern. Wir haben jedoch die Struktur des Projektcodes verbessert, obwohl die Codegröße der
FormComponent
Komponente immer noch recht groß ist. Jetzt löst dieser Code jedoch nur ein Problem, er ist nur für die Visualisierung des Formulars verantwortlich. Daher ist die Arbeit mit ihm jetzt viel einfacher.
Dadurch haben wir eine Aufgabentrennung zwischen den Komponenten erreicht. Die
Form
Komponente aus der Datei
FormContainer.js
jetzt ausschließlich von der Anwendungslogik belegt, und die
FormComponent
Komponente aus der Datei
FormComponent.js
enthält nur den Code, der die Anwendungsschnittstelle bildet. Die
App
Komponente ist jetzt nur noch für das Zusammenstellen der Seite aus großen Blöcken verantwortlich.
Es ist erwähnenswert, dass angesichts der Existenz von Bibliotheken wie
Redux
und der kürzlich veröffentlichten
Context
API das hier diskutierte Container / Component-Muster nicht mehr so relevant ist wie zuvor. Beispielsweise kann Redux den globalen Status einer Anwendung unterstützen, die Komponenten verwenden können.
Zusammenfassung
In dieser Lektion haben wir die Verwendung des Container / Komponenten-Musters untersucht, das darauf abzielt, die Komponenten in diejenigen zu unterteilen, die für die Bildung der Anwendungsschnittstelle verantwortlich sind, und diejenigen, die für das Speichern des Status und für die Logik der Anwendung verantwortlich sind. Das Anwenden dieses Musters trägt zur Verbesserung der Codestruktur von React-Anwendungen bei und erleichtert den Entwicklungsprozess. Nächstes Mal werden wir an einem Kursprojekt arbeiten.
Liebe Leser! Welche Entwurfsmuster verwenden Sie bei der Entwicklung von React-Anwendungen?
