刚开始写React代码的时候,看到组件的编写方式有很多种,不同教程讲授的内容也大不相同。尽管该框架从那时起已经相当成熟,但并没有一个固定的“正确”方法来实现它。在MuseFind工作的那一年,我们团队写了很多React组件,后来我们对方法进行了优化,直到我们满意为止。本指南描述了我们推荐的最佳实践,无论您是初学者还是经验丰富的老手,希望对您有所帮助。在我们开始之前,有几点需要注意:我们使用的是ES6和ES7语法。如果您不清楚真实组件和容器组件的区别,建议先阅读本文。如果您有任何建议、问题或印象,请通过评论告诉我们。基于类的组件基于类的组件具有丰富的状态并且可以包含方法。我们希望尽可能少地使用它们,因为它们也有特定的应用。让我们使用逐行代码逐步构建我们的组件。引入CSSimportReact,{Component}from'react'import{observer}from'mobx-react'importExpandableFormfrom'./ExpandableForm'import'./styles/ProfileContainer.css'我喜欢在JavaScript中操作CSS,理论上它确实有效。不过,这仍然是一个新想法,目前还没有出现切实可行的解决方案。不过在此之前,我们可以先为每个组件引入一个CSS文件。我们还通过编写单独的行将依赖导入与本地导入分开。状态初始化importReact,{Component}from'react'import{observer}from'mobx-react'importExpandableFormfrom'./ExpandableForm'import'./styles/ProfileContainer.css'exportdefaultclassProfileContainerextendsComponent{state={expanded:false}propTypes和defaultPropsimportReact,{Component}from'react'import{observer}from'mobx-react'importExpandableFormfrom'./ExpandableForm'import'./styles/ProfileContainer.css'exportdefaultclassProfileContainerextendsComponent{state={expanded:false}staticpropTypes={model:React.PropTypes.object.isRequired,title:React.PropTypes.string}staticdefaultProps={model:{id:0},title:'YourName'}propTypes和defaultProps是静态属性,应该在组件代码中声明得尽可能高。它们作为文档放置在显着位置,其他开发人员阅读该文件时可以立即看到。所有组件都应该有propTypes。方法importReact,{Component}from'react'import{observer}from'mobx-react'importExpandableFormfrom'./ExpandableForm'import'./styles/ProfileContainer.css'exportdefaultclassProfileContainerextendsComponent{state={expanded:false}staticpropTypes={model:React.PropTypes.object.isRequired,title:React.PropTypes.string}staticdefaultProps={model:{id:0},title:'YourName'}handleSubmit=(e)=>{e.preventDefault()this.props.model.save()}handleNameChange=(e)=>{this.props.model.name=e.target.value}handleExpand=(e)=>{e.preventDefault()this.setState({expanded:!this.state.expanded})}对于类组件,当你想给子组件传递方法时,你必须确认它们被调用时持有的this对象是正确的。这通常可以通过将this.handleSubmit.bind(this)传递给子组件来实现。我们认为这种方式更简洁、更容易,借助ES6箭头功能可以自动维护正确的上下文。属性分析importReact,{Component}from'react'import{observer}from'mobx-react'importExpandableFormfrom'./ExpandableForm'import'./styles/ProfileContainer.css'exportdefaultclassProfileContainerextendsComponent{state={expanded:false}staticpropTypes={model:React.PropTypes.object.isRequired,title:React.PropTypes.string}staticdefaultProps={model:{id:0},title:'YourName'}handleSubmit=(e)=>{e.preventDefault()这个。props.model.save()}handleNameChange=(e)=>{this.props.model.name=e.target.value}handleExpand=(e)=>{e.preventDefault()this.setState(prevState=>({expanded:!prevState.expanded}))}render(){const{model,title}=this.propsreturn({title}
)}}拥有许多属性的组件要让每个属性都在一个新行开始,如上所示。装饰器@observerexportdefaultclassProfileContainerextendsComponent{如果你使用mobx之类的东西,你可以像上面那样装饰类组件—这与将组件传递给函数是一样的。装饰器是一种灵活且可读的方式来修改组件的功能。我们广泛使用它,包括mobx和我们自己的mobx-models库。如果你不想使用装饰器,你可以这样做:classProfileContainerextendsComponent{//Componentcode}exportdefaultobserver(ProfileContainer)closure为了避免将新的闭包传递给子组件,如下所示:{model.name=e.target.value}}//^Notthis.Usethebelow:onChange={this.handleChange}placeholder="YourName"/>原因:每次parentcomponentrenders,创建一个新函数并将其传递给输入。如果输入是一个React组件,它会自动触发它重新渲染,而不管它的其他属性是否真的发生了变化。协调是React中最昂贵的部分,所以不要让它在计算上变得比它需要的更困难!此外,传递类方法更易于阅读、调试和修改。下面是完整的组件代码:importReact,{Component}from'react'import{observer}from'mobx-react'//SeparatelocalimportsfromdependenciesimportExpandableFormfrom'./ExpandableForm'import'./styles/ProfileContainer.css'//Usedecoratorsifneeded@observerexportdefaultclassProfileContainerextendsComponent{state={expanded:false}//在这里初始化state(ES7)orinaconstructormethod(ES6)//DeclarepropTypesasstaticpropertiesasearlyaspossiblestaticpropTypes={model:React.PropTypes.object.isRequired,title:React.PropTypes.string}//DefaultpropsbelowpropTypesstaticdefaultProps={model:{id:0},title:'YourName'}//使用fatarrowfunctionsformethodstopreservecontext(thiswillthusbethecomponentinstance)handleSubmit=(e)=>{e.preventDefault()this.props.model.save()}handleNameChange=(e)=>{this.props.model.name=e.target.value}handleExpand=(e)=>{e.preventDefault()this.setState(prevState=>({expanded:!prevState.expanded}))}render(){//Destructurepropsforreadabilityconst{模型,标题}=this.propsreturn(//Newlinepropsiftherearemorethantwo{title}
{model.name=e.target.value}}//Avoidcreatingnewclosingintherendermethod-usemethodslikebelowonChange={this.handleNameChange}placeholder="你的名字"/>)}}功能组件这些组件没有状态和方法,它们是纯组件,易于理解。尽可能多地使用它们。propTypesimportReactfrom'react'import{observer}from'mobx-react'import'./styles/Form.css'constexpandableFormRequiredProps={onSubmit:React.PropTypes.func.isRequired,expanded:React.PropTypes.bool}//ComponentdeclarationExpandableForm.propTypes=expandableFormRequiredProps在这里,我们将propTypes分配给顶行的变量。在组件声明下方,我们正常分配它们。破坏Props和defaultPropsimportReactfrom'react'import{observer}from'mobx-react'import'./styles/Form.css'constexpandableFormRequiredProps={onSubmit:React.PropTypes.func.isRequired,expanded:React.PropTypes.bool}functionExpandableForm(props){return({props.children}展开)}我的组件是一个函数,所以它的属性可以看作是参数。我们可以像这样扩展它们:.bool}functionExpandableForm({onExpand,expanded=false,children}){return({children}Expand)}请注意,我们也可以使用默认参数以一种可读性很强的方式来扮演defaultProps的角色。如果expanded未定义,我们将其设置为false。(这个例子有点勉强,因为它是一个布尔值,但是对于避免对象的“Cannotreadofundefined”等错误非常有用)。避免以下ES6语法:constExpandableForm=({onExpand,expanded,children})=>{看起来很现代,但这里的函数实际上并没有排序。如果Bable设置正确,这样的名称是可以的——但如果设置不正确,任何错误都会显示在<>中,调试起来会非常麻烦。未命名的函数也会导致React测试库Jest出现问题。为了避免潜在的并发症,我们建议使用function而不是const。封装因为函数式组件中不能使用装饰器,你可以简单地将其作为参数传入函数:importReactfrom'react'import{observer}from'mobx-react'import'./styles/Form.css'constexpandableFormRequiredProps={onExpand:React.PropTypes.func.isRequired,expanded:React.PropTypes.bool}functionExpandableForm({onExpand,expanded=false,children}){return({children}Expand)}ExpandableForm.propTypes=expandableFormRequiredPropsexportdefaultobserver(ExpandableForm)下面是完整的组件代码:importReactfrom'react'import{observer}from'mobx-react'//Separatelocalimportsfromdependenciesimport'./styles/Form.css'//DeclarepropTypeshereasvariable,thenassignbelowfunctiondeclaration//YouwantthesettobeasvisibleaspossibleconstexpandableFormRequiredProps={onSubmit:React.PropTypes.func.isRequired,expanded:React.PropTypes.bodefaultol这样的}//Destructurepropslike论证设置默认道具功能可扩展形式({onExpand,expanded=false,children}){return({children}展开)}//SetpropTypesdownheretothosedeclaredaboveExpandableForm.propTypes=expandableFormRequiredProps//Wrapthecomponentinsteadofdecoratingitexportdefaultobserver(ExpandableForm)JSX中的条件分支你将有很多机会做很多条件分支好主意。有一些库可以解决这个问题(JSX-ControlStatements),但是与其引入额外的依赖关系,不如通过以下方式解决复杂条件分支的问题:用花括号包裹一个IIFE,然后把其中有一个if语句,它可以返回您想要呈现的任何内容。请注意,像这样的IIFE会影响性能,但在大多数情况下我们选择为此降低可读性还不够。还有,当只想在条件分支中渲染某个元素时,使用短路写法更划算:{isTrue&&True!
}