Tutorial Reagir Parte 26: Arquitetura de Aplicativos, Padrão de Container / Componente

Nesta parte da tradução do tutorial do React, falaremos sobre a arquitetura dos aplicativos React. Em particular, discutiremos o popular padrão Container / Component.

imagem

Parte 1: visão geral do curso, razões para a popularidade do React, ReactDOM e JSX
Parte 2: componentes funcionais
Parte 3: arquivos de componentes, estrutura do projeto
Parte 4: componentes pai e filho
Parte 5: início do trabalho em um aplicativo TODO, noções básicas de estilo
Parte 6: sobre alguns recursos do curso, JSX e JavaScript
Parte 7: estilos embutidos
Parte 8: trabalho contínuo na aplicação TODO, familiaridade com as propriedades dos componentes
Parte 9: propriedades do componente
Parte 10: Workshop sobre como trabalhar com propriedades e estilo de componentes
Parte 11: geração dinâmica de marcação e método de matrizes de mapas
Parte 12: workshop, terceira etapa do trabalho em uma aplicação TODO
Parte 13: componentes baseados em classe
Parte 14: workshop sobre componentes baseados em classe, status dos componentes
Parte 15: oficinas de saúde componentes
Parte 16: a quarta etapa do trabalho em um aplicativo TODO, manipulação de eventos
Parte 17: quinta etapa do trabalho em um aplicativo TODO, modificando o estado dos componentes
Parte 18: a sexta etapa do trabalho em um aplicativo TODO
Parte 19: métodos do ciclo de vida dos componentes
Parte 20: a primeira lição sobre renderização condicional
Parte 21: segunda lição e workshop sobre renderização condicional
Parte 22: sétima etapa do trabalho em um aplicativo TODO, baixando dados de fontes externas
Parte 23: primeira lição sobre como trabalhar com formulários
Parte 24: Segunda lição sobre formulários
Parte 25: Workshop sobre como trabalhar com formulários
Parte 26: arquitetura do aplicativo, padrão Container / Component
Parte 27: projeto do curso

Lição 44. Arquitetura de Aplicativos, Padrão de Container / Componente


Original

Às vezes, a quantidade de trabalho pela qual um componente separado é responsável é muito grande; um componente precisa resolver muitas tarefas. O uso do padrão Container / Component permite separar a lógica do aplicativo da lógica da formação de sua representação visual. Isso permite que você melhore a estrutura do aplicativo, compartilhe a responsabilidade pelo desempenho de várias tarefas entre diferentes componentes.

Na lição prática anterior, criamos um enorme componente cujo comprimento de código se aproxima de 150 linhas. Aqui está o código que temos então:

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 

A primeira desvantagem desse código, que imediatamente chama sua atenção, é que, ao trabalhar com ele, você constantemente precisa rolar o código na janela do editor.

Você pode perceber que a maior parte desse código é a lógica da formação da interface do aplicativo, o conteúdo do método render() . Além disso, uma certa quantidade de código é responsável pela inicialização do estado do componente. O componente também possui o que é chamado de "lógica de negócios" (ou seja, o que implementa a lógica do funcionamento do aplicativo). Este é o código do método handleChange() .

De acordo com os resultados de alguns estudos, sabe-se que a capacidade de um programador de perceber o código que ele está vendo é muito prejudicada se o código for longo o suficiente, e o programador precisa usar a rolagem para visualizá-lo por inteiro. Eu notei isso durante as aulas. Quando o código que estou falando acaba sendo bastante longo, e eu constantemente tenho que percorrê-lo, fica mais difícil para os alunos percebê-lo.

Seria bom se refizéssemos nosso código, compartilhando a responsabilidade entre os diferentes componentes para a formação da interface do aplicativo (o que agora é descrito no método render() ) e para a implementação da lógica do aplicativo, ou seja, por definição de como ele deve parecer interface (o código correspondente agora é representado pelo construtor do componente no qual o estado é inicializado e pelo manipulador de eventos de controle handleChange() ). Ao usar essa abordagem para o design do aplicativo, de fato, trabalhamos com dois tipos de componentes e deve-se observar que você pode encontrar nomes diferentes para esses componentes.

Usaremos o padrão Container / Component aqui. Ao usá-lo, os aplicativos são construídos dividindo os componentes em dois tipos - componentes (a palavra Container no nome do padrão se refere a eles) e componentes de apresentação (este é Component no nome do padrão). Às vezes, os componentes do contêiner são chamados de componentes "inteligentes", ou simplesmente "contêineres", e os componentes de apresentação são chamados de componentes "burros", ou simplesmente "componentes". Existem outros nomes para esses tipos de componentes e, note-se, o significado contido nesses nomes pode, caso a caso, diferir em determinados recursos. Em geral, a idéia geral dessa abordagem é que temos um componente de contêiner responsável por armazenar estados e contendo métodos para gerenciar estados, e a lógica de formar a interface é transferida para outro componente de apresentação. Este componente é responsável apenas por receber propriedades do componente de contêiner e pela formação correta da interface.

Aqui está o material de Dan Abramov no qual ele explora essa idéia.

Transformamos o código de nossa aplicação de acordo com o padrão Container / Component.
Primeiro, vamos prestar atenção ao fato de que agora tudo no aplicativo é montado em um único componente do App . Esse aplicativo foi projetado de maneira a simplificar sua estrutura o máximo possível, mas em projetos reais o componente App dificilmente transfere a tarefa de renderizar o formulário e incluir nele o código destinado à organização do trabalho dos mecanismos internos desse formulário.

Adicione à mesma pasta em que o arquivo App.js está localizado, o arquivo Form.js , no qual o código do novo componente estará localizado. Transferimos todo o código do componente App para este arquivo e convertemos o componente App , que agora é representado por um componente baseado na classe, em um componente funcional, cuja principal tarefa será a saída do componente Form . Não se esqueça de importar o componente Form para o componente App . Como resultado, o código do componente App ficará assim:

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

Aqui está o que o aplicativo exibe na tela nesta fase do trabalho.


Aplicativo no navegador

Nas aulas anteriores, eu disse a você que prefiro que o componente App seja algo como um "índice" do aplicativo, que indica em que ordem suas seções são exibidas na página, representadas por outros componentes aos quais são delegadas tarefas de renderização de grandes fragmentos do aplicativo.

Melhoramos um pouco a estrutura do aplicativo, mas o principal problema, expresso no fato de que um componente tem muita responsabilidade, ainda não foi resolvido. Simplesmente transferimos tudo o que havia anteriormente no componente App para o componente Form . Portanto, agora vamos resolver esse problema. Para fazer isso, crie, na mesma pasta em que os arquivos Form.js e App.js , outro arquivo - FormComponent.js . Este arquivo representará o componente de apresentação responsável pela visualização do formulário. De fato, você pode nomear de maneira diferente, pode estruturar arquivos de componentes de maneira diferente, tudo depende das necessidades e da escala de um projeto específico. O arquivo Form.js conterá a lógica do formulário, ou seja, o código do componente do contêiner. Portanto, renomeie-o para FormContainer.js e altere o comando import no código do componente App , trazendo-o para este formulário:

 import Form from "./FormContainer" 

Você também pode renomear o componente Form para FormContainer , mas não faremos isso. Agora, transferiremos o código responsável pela renderização do formulário do arquivo FormContainer.js para o arquivo FormComponent.js .

O componente FormComponent será funcional. Aqui está como o código dele ficará nesta etapa do trabalho:

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

Se você observar esse código, fica claro que não podemos nos limitar a simplesmente transferi-lo de arquivo para arquivo, já que agora existem links para o estado (por exemplo, this.state.firstName ) e o manipulador de eventos ( this.handleChange ), que costumava estar no mesmo componente com base na classe em que esse código de renderização estava. Agora, tudo o que foi obtido anteriormente da mesma classe em que o código de renderização estava localizado será obtido das propriedades passadas para o componente. Existem outros problemas. Agora vamos corrigir esse código, mas primeiro retornaremos ao código do componente Form , que está agora no arquivo FormContainer.js .

Seu método render() agora está vazio. Precisamos que o componente FormComponent seja exibido neste método e precisamos organizar a transferência das propriedades necessárias para ele. Importamos o FormComponent para o arquivo Form e exibimos o FormComponent no método render() , passando um manipulador de eventos e, como objeto, estado. Agora, o código do componente Form ficará assim:

 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 

Vamos FormComponent código do componente FormComponent , trazendo-o para o seguinte formulário:

 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 

Aqui, corrigimos o código levando em consideração o fato de que o componente agora recebe dados e um link para o manipulador de eventos por meio de propriedades.

Depois de todas essas transformações, nem a aparência do formulário nem a maneira como ele funciona serão alteradas, mas melhoramos a estrutura do código do projeto, embora o tamanho do código do componente FormComponent ainda seja bastante grande. No entanto, agora esse código resolve apenas um problema, ele é responsável apenas pela visualização do formulário. Portanto, trabalhar com ele agora é muito mais fácil.

Como resultado, conseguimos uma separação de responsabilidades entre os componentes. O componente Form do arquivo FormContainer.js agora FormContainer.js ocupado exclusivamente pela lógica do aplicativo, e o componente FormComponent.js arquivo FormComponent.js contém apenas o código que forma a interface do aplicativo. O componente App agora é responsável apenas pela montagem da página a partir de blocos grandes.

Vale ressaltar que, dada a existência de bibliotecas como Redux e a API de Context recentemente lançada, o padrão Container / Componente discutido aqui não é mais tão relevante quanto antes. Por exemplo, o Redux pode suportar o estado global de um aplicativo que os componentes podem usar.

Sumário


Nesta lição, examinamos o uso do padrão Container / Component, cujo objetivo é dividir os componentes nos responsáveis ​​pela formação da interface do aplicativo e naqueles responsáveis ​​pelo armazenamento do estado e pela lógica do aplicativo. A aplicação desse padrão ajuda a melhorar a estrutura de código dos aplicativos React e facilita o processo de desenvolvimento. Da próxima vez, trabalharemos em um projeto de curso.

Caros leitores! Quais padrões de design você usa ao desenvolver aplicativos React?

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


All Articles