Tutoriel React, partie 26: Architecture d'application, modèle de conteneur / composant

Dans cette partie de la traduction du didacticiel React, nous parlerons de l'architecture des applications React. En particulier, nous discuterons du modèle de conteneur / composant populaire.

image

Partie 1: aperçu du cours, raisons de la popularité de React, ReactDOM et JSX
Partie 2: composants fonctionnels
Partie 3: fichiers composants, structure du projet
Partie 4: composants parent et enfant
Partie 5: début des travaux sur une application TODO, les bases du style
Partie 6: sur certaines fonctionnalités du cours, JSX et JavaScript
Partie 7: styles en ligne
Partie 8: poursuite des travaux sur l'application TODO, familiarité avec les propriétés des composants
Partie 9: propriétés des composants
Partie 10: Atelier sur l'utilisation des propriétés et du style des composants
Partie 11: génération de balisage dynamique et méthode des tableaux de cartes
Partie 12: atelier, troisième étape de travail sur une application TODO
Partie 13: composants basés sur les classes
Partie 14: atelier sur les composants basés sur les classes, état des composants
Partie 15: ateliers santé composante
Partie 16: quatrième étape de travail sur une application TODO, gestion d'événements
Partie 17: cinquième étape de travail sur une application TODO, modifiant l'état des composants
Partie 18: la sixième étape de travail sur une application TODO
Partie 19: méthodes du cycle de vie des composants
Partie 20: la première leçon de rendu conditionnel
Partie 21: deuxième leçon et atelier sur le rendu conditionnel
Partie 22: la septième étape des travaux sur une application TODO, téléchargement de données depuis des sources externes
Partie 23: première leçon sur l'utilisation des formulaires
Partie 24: Deuxième leçon sur les formulaires
Partie 25: Atelier sur l'utilisation des formulaires
Partie 26: architecture d'application, modèle de conteneur / composant
Partie 27: projet de cours

Leçon 44. Architecture d'application, modèle de conteneur / composant


Original

Parfois, la quantité de travail dont un composant distinct est responsable est trop importante; un composant doit résoudre trop de tâches. L'utilisation du modèle Conteneur / Composant vous permet de séparer la logique de l'application de la logique de formation de sa représentation visuelle. Cela vous permet d'améliorer la structure de l'application, de partager la responsabilité de l'exécution des différentes tâches entre les différents composants.

Lors de la précédente leçon pratique, nous avons créé un énorme composant dont la longueur de code approche 150 lignes. Voici le code que nous avons obtenu alors:

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 

Le premier inconvénient de ce code, qui attire immédiatement l'attention, est que lorsque vous travaillez avec lui, vous devez constamment le faire défiler dans la fenêtre de l'éditeur.

Vous pouvez remarquer que la majeure partie de ce code est la logique de formation de l'interface d'application, le contenu de la méthode render() . De plus, une certaine quantité de code est responsable de l'initialisation de l'état du composant. Le composant possède également ce qu'on appelle la «logique métier» (c'est-à-dire ce qui met en œuvre la logique du fonctionnement de l'application). Il s'agit du code de la méthode handleChange() .

Selon les résultats de certaines études, il est connu que la capacité d'un programmeur à percevoir le code qu'il regarde est grandement altérée si le code est suffisamment long, et le programmeur doit utiliser le défilement pour l'afficher dans son intégralité. Je l'ai remarqué pendant les cours. Lorsque le code dont je parle se révèle assez long et que je dois constamment le parcourir, il devient plus difficile pour les étudiants de le percevoir.

Ce serait bien si nous retravaillions notre code, en partageant la responsabilité entre les différents composants pour la formation de l'interface d'application (ce qui est maintenant décrit dans la méthode render() ) et pour l'implémentation de la logique d'application, c'est-à-dire par définition de son apparence interface (le code correspondant est maintenant représenté par le constructeur du composant dans lequel l'état est initialisé et le gestionnaire d'événements de contrôle handleChange() ). Lorsque vous utilisez cette approche de la conception d'applications, nous travaillons en fait avec deux types de composants, et il convient de noter que vous pouvez rencontrer des noms différents pour ces composants.

Nous utiliserons ici le modèle Conteneur / Composant. Lorsque vous l'utilisez, les applications sont construites en divisant les composants en deux types - composants de composant (le mot conteneur dans le nom du modèle se réfère à eux) et composants de présentation (il s'agit de composant dans le nom du modèle). Parfois, les composants de conteneur sont appelés composants «intelligents», ou simplement «conteneurs», et les composants de présentation sont appelés composants «stupides», ou simplement «composants». Il existe d'autres noms pour ces types de composants et, il convient de noter que la signification qui y est contenue peut, selon les cas, différer dans certaines fonctionnalités. En général, l'idée générale de cette approche est que nous avons un composant conteneur qui est responsable du stockage de l'état et des méthodes de gestion de l'état, et la logique de formation de l'interface est transférée à un autre - composant de présentation. Ce composant est uniquement responsable de la réception des propriétés du composant conteneur et de la formation correcte de l'interface.

Voici le matériel de Dan Abramov dans lequel il explore cette idée.

Nous transformons le code de notre application conformément au modèle Container / Component.
Tout d'abord, faisons attention au fait que maintenant tout dans l'application est assemblé en un seul composant de l' App . Cette application est conçue pour simplifier au maximum sa structure, mais dans les projets réels, le composant App n'a guère de sens de transférer la tâche de rendu du formulaire et d'y inclure du code conçu pour organiser le travail des mécanismes internes de ce formulaire.

Ajoutez au même dossier dans lequel se trouve le fichier Form.js , le fichier Form.js , dans lequel se Form.js le code du nouveau composant. Nous transférons tout le code du composant App dans ce fichier et convertissons le composant App , qui est maintenant représenté par le composant basé sur la classe, en un composant fonctionnel, dont la tâche principale sera la sortie du composant Form . N'oubliez pas d'importer le composant Form dans le composant App . Par conséquent, le code du composant App ressemblera à ceci:

 import React, {Component} from "react" import Form from "./Form" function App() {   return (       <Form />   ) } export default App 

Voici ce que l'application affiche à l'écran à ce stade du travail.


Application dans le navigateur

Dans les cours précédents, je vous ai dit que je préfère que le composant App soit quelque chose comme une «table des matières» de l'application, qui indique dans quel ordre ses sections sont affichées sur la page, représentées par d'autres composants qui sont des tâches déléguées de rendu de gros fragments de l'application.

Nous avons légèrement amélioré la structure de l'application, mais le principal problème, exprimé dans le fait qu'un composant a trop de responsabilités, n'a pas encore été résolu. Nous avons simplement transféré tout ce qui était auparavant dans le composant App vers le composant Form . Par conséquent, nous allons maintenant résoudre ce problème. Pour ce faire, créez, dans le même dossier dans lequel se Form.js les fichiers Form.js et App.js , un autre fichier - FormComponent.js . Ce fichier représentera le composant de présentation responsable de la visualisation du formulaire. En fait, vous pouvez le nommer différemment, vous pouvez structurer les fichiers de composants différemment, tout dépend des besoins et de l'échelle d'un projet particulier. Le fichier Form.js contiendra la logique du formulaire, c'est-à-dire le code du composant conteneur. Par conséquent, renommez-le FormContainer.js et modifiez la commande d'importation dans le code du composant App , en l'amenant à ce formulaire:

 import Form from "./FormContainer" 

Vous pouvez également renommer le composant Form en FormContainer , mais nous ne le ferons pas. Nous allons maintenant transférer le code responsable du rendu du formulaire du fichier FormContainer.js vers le fichier FormComponent.js .

Le composant FormComponent sera fonctionnel. Voici à quoi ressemblera son code à cette étape du travail:

 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>   ) } 

Si vous regardez ce code, il devient clair que nous ne pouvons pas nous limiter à le transférer simplement d'un fichier à un autre, car il existe maintenant des liens vers l'état (par exemple, this.state.firstName ) et le gestionnaire d'événements ( this.handleChange ), qui était dans le même composant en fonction de la classe dans laquelle se trouvait ce code de rendu. Désormais, tout ce qui a été précédemment extrait de la même classe dans laquelle se trouvait le code de rendu sera extrait des propriétés transmises au composant. Il y a d'autres problèmes. Nous allons maintenant corriger ce code, mais nous allons d'abord revenir au code du composant Form , qui se trouve maintenant dans le fichier FormContainer.js .

Sa méthode render() est désormais vide. Nous avons besoin que le composant FormComponent soit affiché dans cette méthode et nous devons y organiser le transfert des propriétés nécessaires. Nous importons le FormComponent dans le fichier Form et FormComponent le FormComponent dans la méthode render() , en lui passant un gestionnaire d'événements et, en tant qu'objet, l'état. Maintenant, le code du composant Form ressemblera à ceci:

 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 

Nous FormComponent code du composant FormComponent , en le portant sous la forme suivante:

 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 

Ici, nous avons corrigé le code en tenant compte du fait que le composant reçoit désormais des données et un lien vers le gestionnaire d'événements via les propriétés.

Après toutes ces transformations, ni l'apparence du formulaire ni son fonctionnement ne changeront, mais nous avons amélioré la structure du code du projet, bien que la taille du code du composant FormComponent soit encore assez grande. Cependant, maintenant ce code ne résout qu'un seul problème, il n'est responsable que de la visualisation du formulaire. Par conséquent, travailler avec lui est maintenant beaucoup plus facile.

En conséquence, nous avons réalisé une séparation des responsabilités entre les composants. Le composant Form du fichier FormContainer.js désormais exclusivement occupé par la logique d'application, tandis que le composant FormComponent.js fichier FormComponent.js contient uniquement le code qui forme l'interface d'application. Le composant App est désormais uniquement responsable de l'assemblage de la page à partir de gros blocs.

Il convient de noter que, compte tenu de l'existence de bibliothèques comme Redux et de l'API Context récemment publiée, le modèle Container / Component discuté ici n'est plus aussi pertinent qu'auparavant. Par exemple, Redux peut prendre en charge l'état global d'une application que les composants peuvent utiliser.

Résumé


Dans cette leçon, nous avons examiné l'utilisation du modèle Conteneur / Composant, qui vise à diviser les composants en ceux qui sont responsables de la formation de l'interface de l'application et ceux qui sont responsables du stockage de l'état et de la logique de l'application. L'application de ce modèle permet d'améliorer la structure du code des applications React et facilite le processus de développement. La prochaine fois, nous travaillerons sur un projet de cours.

Chers lecteurs! Quels modèles de conception utilisez-vous lors du développement d'applications React?

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


All Articles