在React教程的翻译的这一部分中,我们将讨论React应用程序的体系结构。 特别是,我们将讨论流行的Container / Component模式。

→
第1部分:课程概述,React,ReactDOM和JSX普及的原因→
第2部分:功能组件→
第3部分:组件文件,项目结构→
第4部分:父级和子级组件→
第5部分:TODO应用程序的开始工作,样式设计的基础→
第6部分:关于本课程的一些功能,JSX和JavaScript→
第7部分:内联样式→
第8部分:继续研究TODO应用程序,熟悉组件的属性→
第9部分:组件属性→
第10部分:使用组件特性和样式的研讨会→
第11部分:动态标记生成和映射数组方法→
第12部分:研讨会,TODO应用程序的第三阶段工作→
第13部分:基于类的组件→
第14部分:关于基于类的组件,组件状态的研讨会→
第15部分:组件健康研讨会→
第16部分:TODO应用程序的第四阶段工作,事件处理→
第17部分:TODO应用程序的第五阶段工作,修改组件的状态→
第18部分:TODO应用程序的第六阶段工作→
第19部分:组件生命周期方法第20部分:条件渲染的第一课→
第21部分:关于条件渲染的第二课和研讨会→
第22部分:TODO应用程序的第七阶段工作,从外部资源下载数据→
第23部分:关于使用表格的第一课→
第24部分:第二形式课→
第25部分:使用表单的研讨会→
第26部分:应用程序体系结构,容器/组件模式→
第27部分:课程项目课44.应用程序体系结构,容器/组件模式
→
原创有时,一个单独的组件负责的工作量太大;一个组件必须解决太多的任务。 使用Container / Component模式可以使您将应用程序的逻辑与可视化表示形式的逻辑分开。 这使您可以改善应用程序的结构,在不同组件之间分担执行各种任务的责任。
在上一期实践课程中,我们创建了一个巨大的组件,其代码长度接近150行。 这是我们当时得到的代码:
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
该代码的第一个缺点立即引起您的注意,是在使用它时,必须不断在编辑器窗口中滚动它。
您会注意到,这段代码的大部分是构成应用程序接口的逻辑,即
render()
方法的内容。 另外,一定数量的代码负责初始化组件的状态。 该组件还具有所谓的“业务逻辑”(即,实现应用程序功能的逻辑的东西)。 这是
handleChange()
方法的代码。
根据一些研究的结果,众所周知,如果代码足够长,那么程序员感知他正在查看的代码的能力就会大大受损,并且程序员必须使用滚动才能完整地查看它。 我在上课时注意到了这一点。 当我在说的代码很长,而且我不得不不断滚动浏览代码时,学生将很难理解它。
如果我们重新编写代码,在不同组件之间分担责任,以形成应用程序接口(现在在
render()
方法中进行了描述)以及实现应用程序逻辑,即通过定义其外观,那将是很好的接口(现在,相应的代码由初始化状态的组件的构造函数和
handleChange()
控制事件处理程序表示)。 当使用这种方法进行应用程序设计时,实际上,我们使用两种类型的组件,但应注意的是,您可能会为这些组件使用不同的名称。
我们将在此处使用“容器/组件”模式。 使用它时,通过将组件分为两种类型来构建应用程序-容器组件(模式名称中的单词Container指的是容器组件)和表示组件(即模式名称中的Component)。 有时将容器组件称为“智能”组件,或简称为“容器”,将表示组件称为“哑巴”组件,或简称为“组件”。 这些类型的组件还有其他名称,应该注意的是,这些名称中包含的含义在某些情况下可能会有所不同。 通常,此方法的总体思路是,我们有一个容器组件负责存储状态并包含用于管理状态的方法,并且将形成接口的逻辑转移到另一个表示组件上。 该组件仅负责接收来自容器组件的属性并负责接口的正确形成。
→
这是丹·阿布拉莫夫
( Dan Abramov
)的材料,他在其中探索了这个想法。
我们根据“容器/组件”模式转换应用程序的代码。
首先,让我们注意一个事实,现在应用程序中的所有组件都组装在
App
的单个组件中。 该应用程序的设计方式是尽可能简化其结构,但在实际项目中,
App
组件几乎没有任何意义来转移呈现表单的任务并在其中包含旨在组织此表单内部机制的代码。
将文件
App.js
添加到文件
App.js
所在的文件
Form.js
,新组件的代码将位于该文件夹中。 我们将所有代码从
App
组件传输到此文件,然后将现在由基于类的组件表示的
App
组件转换为功能组件,其主要任务将是
Form
组件的输出。 不要忘记将
Form
组件导入
App
组件。 结果,
App
组件的代码将如下所示:
import React, {Component} from "react" import Form from "./Form" function App() { return ( <Form /> ) } export default App
这是应用程序在此工作阶段在屏幕上显示的内容。
浏览器中的应用在之前的课程中,我告诉您,我希望
App
组件像
App
的“目录”一样,它指示其部分在页面上显示的顺序,由其他组件代表,这些组件是渲染应用程序大片段的委托任务。
我们对应用程序的结构进行了一些改进,但是主要问题(一个组件承担的责任过多)尚未得到解决。 我们只是将
App
组件中的所有内容都转移到了
Form
组件中。 因此,现在我们要解决这个问题。 为此,请在
Form.js
和
App.js
文件所在的同一文件夹中创建另一个文件
FormComponent.js
。 该文件将代表负责可视化表单的表示组件。 实际上,您可以使用不同的名称命名,也可以使用不同的结构来构造组件文件,这都取决于特定项目的需求和范围。
Form.js
文件将包含表单的逻辑,即容器组件的代码。 因此,将其重命名为
FormContainer.js
并在
App
组件的代码中更改import命令,将其转换为以下形式:
import Form from "./FormContainer"
您也可以将
Form
组件重命名为
FormContainer
,但是我们不会这样做。 现在,我们将负责呈现表单的代码从
FormContainer.js
文件转移到
FormComponent.js
文件。
FormComponent
组件将起作用。 这是他的代码在此工作阶段的外观:
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> ) }
如果您看一下这段代码,很显然,我们不能仅仅局限于将其从一个文件传输到
this.state.firstName
文件,因为现在有了状态(例如
this.state.firstName
)和事件处理程序(
this.handleChange
)的链接,以前根据此渲染代码所在的类位于同一组件中。 现在,以前从呈现代码所在的同一类中获取的所有内容都将从传递给组件的属性中获取。 还有其他一些问题。 现在我们将修复此代码,但首先我们将返回
Form
组件的代码,该组件现在位于文件
FormContainer.js
。
它的
render()
方法现在为空。 我们需要在该方法中显示
FormComponent
组件,并且需要组织必要属性的传递。 我们将
FormComponent
导入到
Form
文件中,并在
render()
方法中显示
FormComponent
,并向其传递事件处理程序和状态(作为对象)。 现在,
Form
组件的代码将如下所示:
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
组件
FormComponent
代码,使其具有以下形式:
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
在这里,我们考虑到组件现在已接收数据以及通过属性链接到事件处理程序的事实,从而修复了代码。
经过所有这些转换后,尽管
FormComponent
组件的代码大小仍然很大,但表单的外观及其工作方式都不会改变,但是我们改进了项目代码的结构。 但是,现在此代码仅解决了一个问题,它仅负责表单的可视化。 因此,现在和他一起工作要容易得多。
结果,我们实现了组件之间的职责分离。 现在,
FormContainer.js
文件中的
Form
组件仅由应用程序逻辑占用,而
FormComponent.js
文件中的
FormComponent
组件仅包含构成应用程序接口的代码。
App
组件现在仅负责从大块中组装页面。
值得注意的是,鉴于存在
Redux
之类的库以及最近发布的
Context
API,此处讨论的Container / Component模式不再像以前那样重要。 例如,Redux可以支持组件可以使用的应用程序的全局状态。
总结
在本课程中,我们研究了“容器/组件”模式的使用,该模式旨在将组件分为那些负责形成应用程序接口的组件和那些负责存储状态和应用程序逻辑的组件。 应用此模式有助于改善React应用程序的代码结构并促进开发过程。 下次我们将进行课程项目。
亲爱的读者们! 在开发React应用程序时使用什么设计模式?
