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.

→
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 composantsPartie 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 coursLeçon 44. Architecture d'application, modèle de conteneur / composant
→
OriginalParfois, 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 navigateurDans 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?
