博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
编写React组件的最佳实践
阅读量:6527 次
发布时间:2019-06-24

本文共 8545 字,大约阅读时间需要 28 分钟。

在我第一次编写 React 代码的时候,我见发现许多不同的方法可以用来编写组件,不同教程教授的内容也大不相同。尽管从那时候起框架已经相当成熟,但并没有一种固定的“正确”方式指导。


在  工作的一年里,我们的团队编写了许多 React 组件,后期我们对方法进行了优化直到满意为止。

本指南描述了我们推荐的最佳实践,不管你是一名初学者还是有经验的老手,希望它能对你有所帮助。

在我们开始之前,有几个地方要注意一下:

  • 我们使用的是 ES6 和 ES7 的语法。

  • 如果你对于现实和容器组件两者之间的区别不甚明了,建议首先阅读一下。

  • 如果有任何建议、疑问或者感想,请通过评论来让我们知晓。

基于类的组件

基于类的组件具有丰富状态而且可以含有方法。我们要尽可能有节制地去使用,因为它们也有特定的适用场合。

让我们使用一行一行的代码逐步地将我们的组件构建起来吧。

引入 CSS

import React, {Component} from 'react'import {observer} from 'mobx-react'import ExpandableForm from './ExpandableForm'import './styles/ProfileContainer.css'

我喜欢,这在理论上这样做是可行的。不过它仍然是一种新的创意,还没出现切实可行的解决方案。不过在此之前,我们可以先为每一个组件引入一个 CSS 文件。

我们也通过另写一行将依赖引入从本地引入独立了出来。

状态初始化

import React, {Component} from 'react'import {observer} from 'mobx-react'import ExpandableForm from './ExpandableForm'import './styles/ProfileContainer.css'export default class ProfileContainer extends Component {  state = { expanded: false }

propTypes 和 defaultProps

import React, {Component} from 'react'import {observer} from 'mobx-react'import ExpandableForm from './ExpandableForm'import './styles/ProfileContainer.css'export default class ProfileContainer extends Component {  state = { expanded: false }   static propTypes = {    model: React.PropTypes.object.isRequired,    title: React.PropTypes.string  }   static defaultProps = {    model: {      id: 0    },    title: 'Your Name'  }

propTypes 和 defaultProps 是静态属性,要在组件代码中尽可能高的位置进行声明。它们作为文档放在醒目的位置,其他开发者阅读此文件时能立即看到。

所有的组件都应该有 propTypes。

方法

import React, {Component} from 'react'import {observer} from 'mobx-react'import ExpandableForm from './ExpandableForm'import './styles/ProfileContainer.css'export default class ProfileContainer extends Component {  state = { expanded: false }   static propTypes = {    model: React.PropTypes.object.isRequired,    title: React.PropTypes.string  }   static defaultProps = {    model: {      id: 0    },    title: 'Your Name'  }  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 的箭头函数可以自动地维护好正确的上线文。

属性析构

import React, {Component} from 'react'import {observer} from 'mobx-react'import ExpandableForm from './ExpandableForm'import './styles/ProfileContainer.css'export default class ProfileContainer extends Component {  state = { expanded: false }   static propTypes = {    model: React.PropTypes.object.isRequired,    title: React.PropTypes.string  }   static defaultProps = {    model: {      id: 0    },    title: 'Your Name'  }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() {    const {      model,      title    } = this.props    return (       
        
          

{title}

          
        
           )   } }

拥有许多属性的组件要让每个属性都另起一行,如上所示。

装饰器

@observerexport default class ProfileContainer extends Component {

如果你使用了一些像  的东西,就可以像上面这样对类组件进行装饰 — 这样做跟将组件传递给一个函数是一样的效果。

是一种用来修改组件功能的灵活且可读性好的方式。我们对其进行了广泛的运用,包括 mobx 还有我们自己的  库。

如果你不想使用装饰器,可以这样做:

class ProfileContainer extends Component {  // Component code}export default observer(ProfileContainer)

闭包

要避免向子组件传递新的闭包,如下:

 { model.name = e.target.value }}  // ^ Not this. Use the below:  onChange={
this.handleChange}  placeholder="Your Name"/>

原因: 每次父组件渲染时,都会有一个新的函数被创建并传递给输入。

如果输入是一个 React 组件,不管它的其它属性实际是否已经发生了变化,都会自动地触发让它重新渲染。

调和是 React 中消耗最昂贵的部分,因此不要让它的计算难度超过所需! 另外,传递一个类方法更容易阅读、调试和修改。

如下是完整的组件代码:

import React, {Component} from 'react'import {observer} from 'mobx-react'// Separate local imports from dependenciesimport ExpandableForm from './ExpandableForm'import './styles/ProfileContainer.css'// Use decorators if needed@observerexport default class ProfileContainer extends Component {  state = { expanded: false }  // Initialize state here (ES7) or in a constructor method (ES6)   // Declare propTypes as static properties as early as possible  static propTypes = {    model: React.PropTypes.object.isRequired,    title: React.PropTypes.string  }  // Default props below propTypes  static defaultProps = {    model: {      id: 0    },    title: 'Your Name'  }  // Use fat arrow functions for methods to preserve context (this will thus be the component instance)  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() {    // Destructure props for readability    const {      model,      title    } = this.props    return (       
        // Newline props if there are more than two         
          

{title}

          
 { model.name = e.target.value }}             // Avoid creating new closures in the render method- use methods like below             onChange={this.handleNameChange}             placeholder="Your Name"/>         
           )   } }

函数式组件

这些组件没有状态和方法。它们就是单纯的组件,容易理解。要尽可能常去使用它们。

propTypes

import React from 'react'import {observer} from 'mobx-react'import './styles/Form.css'const expandableFormRequiredProps = {  onSubmit: React.PropTypes.func.isRequired,  expanded: React.PropTypes.bool}// Component declarationExpandableForm.propTypes = expandableFormRequiredProps

这里,我们将 propTypes 分配给了顶部一行的变量。在组件声明的下面,我们对它们进行了正常的分配。

对 Props 和 defaultProps 进行析构

import React from 'react'import {observer} from 'mobx-react'import './styles/Form.css'const expandableFormRequiredProps = {  onSubmit: React.PropTypes.func.isRequired,  expanded: React.PropTypes.bool}function ExpandableForm(props) {  return (    
      {props.children}       
Expand       )}

我的组件是一个函数,因此可以将它的属性看做是参数。我们可以像下面这样对它们进行扩展:

import React from 'react'import {observer} from 'mobx-react'import './styles/Form.css'const expandableFormRequiredProps = {  onExpand: React.PropTypes.func.isRequired,  expanded: React.PropTypes.bool}function ExpandableForm({ onExpand, expanded = false, children }) {  return (    
      {children}       
Expand       )}

注意,我们也能以一种高度可读的方式使用默认参数来扮演 defaultProps 的角色。如果 expanded 是 undefined, 我们就会将其设置为 false。 (这个例子有点勉强,因为是一个布尔值,不过本身对于避免对象的“Cannot read <property> of undefined“这样的错误是很有用的)。

要避免如下这种 ES6 语法:

const ExpandableForm = ({ onExpand, expanded, children }) => {

看着非常现代,不过这里的函数实际上没有被命令。

这样子的名称在 Bable 进行了正确的设置的情况下是可行的 —  但如果没有正确设置,任何错误都会以在<<anonymous>>中出现的方式显示,调试起来相当麻烦。

无名的函数也会在 Jest 这个 React 测试库中引发问题。为了避免潜在的复杂问题出现,我们建议使用 function 而不是 const。

封装

因为在函数式组件中不能使用装饰器,所以你可以简单地将它传递到函数中充当参数:

import React from 'react'import {observer} from 'mobx-react'import './styles/Form.css'const expandableFormRequiredProps = {  onExpand: React.PropTypes.func.isRequired,  expanded: React.PropTypes.bool}function ExpandableForm({ onExpand, expanded = false, children }) {  return (    
      {children}       
Expand       )}ExpandableForm.propTypes = expandableFormRequiredPropsexport default observer(ExpandableForm)

如下是完整的组件代码:

import React from 'react'import {observer} from 'mobx-react'// Separate local imports from dependenciesimport './styles/Form.css'// Declare propTypes here as a variable, then assign below function declaration // You want these to be as visible as possibleconst expandableFormRequiredProps = {  onSubmit: React.PropTypes.func.isRequired,  expanded: React.PropTypes.bool}// Destructure props like so, and use default arguments as a way of setting defaultPropsfunction ExpandableForm({ onExpand, expanded = false, children }) {  return (    
      {children}       
Expand       )}// Set propTypes down here to those declared aboveExpandableForm.propTypes = expandableFormRequiredProps// Wrap the component instead of decorating itexport default observer(ExpandableForm)

JSX 中的条件分支

你会有不少机会去做许多条件分支渲染。如下是你想要去避免的情况:

嵌套的三元组并非不是好主意。

有一些库可以解决这个问题 (),不过相比引入额外的依赖,通过如下这种方式解决复杂条件分支问题要更好:

使用花括弧封装一个 , 然后在里面放入 if 语句,可以返回任何你想要渲染的东西。注意像这样的 IIFE 对性能会有影响,不过在大多数情况中还不足以让我们为此选择丢掉可读性。

还有就是当你只想要在一个条件分支中渲染一个元素时,比起这样做…

{  isTrue   ? 

True!

   : 
}

… 使用短路写法更划算:

{  isTrue &&     

True!

}

作者:leoxu leoxu

来源:51CTO

转载地址:http://dkibo.baihongyu.com/

你可能感兴趣的文章
TD的访问地址
查看>>
【甘道夫】Apache Hadoop 2.5.0-cdh5.2.0 HDFS Quotas 配额控制
查看>>
一张图看懂normal,static,sealed,abstract 的 区别
查看>>
Task的使用
查看>>
s:iterator巧妙控制跳出循环
查看>>
Serv-U 的升级及数据备份和迁移【转】
查看>>
webstorm无法显示左边文件夹目录的解决方法
查看>>
数字校园-云资源平台 2014.10.26-人人通共享空间
查看>>
为你的网站加上SSL,可以使用HTTPS进行访问
查看>>
软件project--谈项目开发
查看>>
在Android中创建文件
查看>>
爬虫基础
查看>>
JS组件系列——再推荐一款好用的bootstrap-select组件,亲测还不错
查看>>
getopt--parse command line options
查看>>
闭包和OC的block的本质
查看>>
MySQL出现Waiting for table metadata lock的场景浅析
查看>>
C# 语言历史版本特性(C# 1.0到C# 7.1汇总更新)
查看>>
什么是数据埋点?
查看>>
git回滚
查看>>
vue2.0 引用qrcode.js实现获取改变二维码的样式
查看>>