React Tutorial Teil 26: Anwendungsarchitektur, Container- / Komponentenmuster

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.

Bild

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 Komponentenlebenszyklus
Teil 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: Kursprojekt

Lektion 44. Anwendungsarchitektur, Container- / Komponentenmuster


Original

Manchmal 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 Browser

In 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?

Source: https://habr.com/ru/post/de446206/


All Articles