⬆️ ⬇️

React Tutorial, Part 26: Application Architecture, Container / Component Pattern

In this part of the translation of the React course, we’ll talk about the architecture of React applications. In particular, we will discuss the popular Container / Component pattern.



image



Part 1: Course Overview, React, ReactDOM, and JSX Reasons

Part 2: Functional Components

Part 3: Component Files, Project Structure

Part 4: Parent and Child Components

Part 5: Getting Started on a TODO Application, Basics of Styling

Part 6: Some of the features of the course, JSX and JavaScript

Part 7: Inline Styles

Part 8: continued work on the TODO application, familiarity with the properties of the components

Part 9: Component Properties

Part 10: Workshop on working with the properties of components and styling

Part 11: dynamic markup generation and the map array method

Part 12: workshop, the third stage of work on the TODO application

Part 13: Class Based Components

Part 14: Workshop on Class Based Components, Component State

Part 15: workshops on working with the state of components

Part 16: the fourth stage of work on a TODO application, event handling

Part 17: the fifth stage of working on a TODO application, modifying the state of components

Part 18: Sixth Stage of Work on a TODO Application

Part 19: Component Life Cycle Techniques

Part 20: The first lesson in conditional rendering.

Part 21: the second lesson and workshop on conditional rendering

Part 22: the seventh stage of work on a TODO application, loading data from external sources

Part 23: First Form Lesson

Part 24: Second Form Lesson

Part 25: Workshop on working with forms

Part 26: Application Architecture, Container / Component Pattern

Part 27: course project



Lesson 44. Application Architecture, Container / Component Pattern



Original

')

Sometimes the amount of work for which the individual component is responsible is too large, the component has to solve too many tasks. Using the Container / Component pattern allows you to separate the logic of the application from the logic of the formation of its visual presentation. This allows you to improve the structure of the application, to share responsibility for performing various tasks between different components.



In the previous practical lesson, we created a huge component, the length of the code of which approaches 150 lines. Here is the code that we got then:



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 


The first drawback of this code, which immediately catches the eye, is that when working with it it constantly has to be scrolled in the editor window.



You can see that the bulk of this code is the logic of the application interface, the contents of the render() method. In addition, a certain amount of code is responsible for initializing the state of the component. In the component there is also what is called “business logic” (that is, something that implements the logic of the application functioning). This is the code of the handleChange() method.



According to the results of some studies, it is known that the programmer’s ability to perceive the code he is looking at deteriorates greatly if the code is long enough and the programmer has to use scrolling to view it in its entirety. I noticed this in the course of the training. When the code I’m talking about is long enough, and I constantly have to scroll through it, it’s harder for students to take it.



It would be nice if we reworked our code, dividing between different components the responsibility for forming the application interface (what is described in the render() method now) and for implementing the logic of the application, that is, by defining how it should look interface (the corresponding code is now represented by the component constructor, in which the state is initialized, and by the event handler of the handleChange() controls). When using a similar approach to the design of applications, we actually work with two types of components, and it should be noted that you can encounter different names of such components.



We will use the Container / Component pattern here. When it is used, applications are built by dividing the components into two types - into component containers (this includes the word Container in the name of the pattern), and into presentation components (this is the Component in the name of the pattern). Sometimes container components are called “smart” (smart) components, or simply “containers”, and presentational components are called “dumb” (dumb) components, or simply “components”. There are other names for these types of components, and, it should be noted, the meaning that is embedded in these names may, from time to time, be distinguished by certain features. In general, the general idea of ​​the approach under consideration is that we have a container component responsible for storing the state and containing methods for managing the state, and the interface formation logic is passed on to the other component, the presentation component. This component is responsible only for obtaining properties from the container component and for the correct formation of the interface.



Here is Dan Abramov ’s material in which he explores this idea.



Let's transform the code of our application in accordance with the Container / Component pattern.

To begin with, let's pay attention to the fact that now everything in the application is collected in a single component of the App . This application is designed for the sake of maximally simplifying its structure, but in real projects the App component hardly makes sense to transfer the task of rendering a form and include code for organizing the work of the internal mechanisms of this form.



Add, in the same folder as the App.js file, the Form.js file, which will contain the code for the new component. We will transfer all the code from the App component to this file, and the App component that is now represented by the component that is based on the class will be transformed into a functional component whose main task will be to output the Form component. Do not forget to import the Form component into the App component. As a result, the App component code will look like this:



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


This is how it looks at this stage of the work that the application displays on the screen.





Application in browser



In previous lessons, I told you that I prefer the App component to be something like a “table of contents” for the application, which shows the order in which the sections of the application are displayed, represented by other components that are delegated to rendering large parts of the application.



We have slightly improved the structure of the application, but the main problem, which is expressed in the fact that too much responsibility is assigned to one component, has not yet been solved. We simply transferred everything that used to be in the App component to the Form component. Therefore now we will be engaged in the decision of this problem. To do this, create, in the same folder where the Form.js and App.js files are located, another file - FormComponent.js . This file will represent the presentation component responsible for rendering the form. In fact, it can be called differently, it is possible to structure the files of components in a different way, it all depends on the needs and scale of a specific project. The file Form.js will contain the logic of the functioning of the form, that is, the code of the component container. Therefore, we rename it to FormContainer.js and change the import command in the code of the App component, bringing it to this form:



 import Form from "./FormContainer" 


You can also rename the Form component in the FormContainer , but we will not do this. Now let's move the code responsible for rendering the form from the FormContainer.js file to the FormComponent.js file.



The FormComponent component will be functional. Here is what its code will look like at this stage of work:



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


If you look at this code, it becomes clear that we cannot limit ourselves to simply transferring it from a file to a file, since now there are links to the state (for example, this.state.firstName ) and the event handler ( this.handleChange ), which were previously in the same component based on the class in which this rendering code was located. Now everything that was previously taken from the same class in which the rendering code was located will be taken from the properties passed to the component. There are some other problems. Now we will correct this code, but first we will return to the code of the Form component, which is now in the FormContainer.js file.



Its render() method is now empty. We need the FormComponent component to be displayed in this method and we need to organize the transfer of the necessary properties to it. We import the FormComponent into the Form file and FormComponent in the render() method, passing it the event handler, and, as an object, the state. Now the code of the Form component will look like this:



 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 


FormComponent code of the FormComponent component, bringing it to the following form:



 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 


Here we have corrected the code, taking into account that the component now receives data and a link to the event handler through the properties.



After all these transformations, neither the form of the form nor the way it works will change, but we have improved the structure of the project code, although the code size of the FormComponent component is still quite large. However, now this code solves only one task, it is responsible only for the visualization of the form. Therefore, working with him is now much easier.



As a result, we have achieved a shared responsibility between the components. The Form component from the FormContainer.js file FormContainer.js now occupied solely by the application logic, and the FormComponent component from the FormComponent.js file contains only the code that forms the application interface. The App component is now only responsible for assembling a page from large blocks.



It is worth noting here that, given the existence of libraries like Redux and the recently released API Context , the Container / Component pattern discussed here is no longer as relevant as before. For example, using Redux tools you can maintain a global application state that components can use.



Results



In this lesson, we examined the use of the Container / Component pattern, which aims to separate components into those that are responsible for shaping the application interface, and those that are responsible for storing the state and for the logic of the application. The application of this pattern helps to improve the code structure of React-applications and facilitates the development process. Next time we will work on a course project.



Dear readers! What design patterns do you use when developing React applications?



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



All Articles