Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[译]React Component最佳实践 #7

Open
YutHelloWorld opened this issue Aug 16, 2017 · 5 comments
Open

[译]React Component最佳实践 #7

YutHelloWorld opened this issue Aug 16, 2017 · 5 comments
Labels

Comments

@YutHelloWorld
Copy link
Owner

YutHelloWorld commented Aug 16, 2017

原文:Our Best Practices for Writing React Components .
这里意译。有些点在之前的文章里提到过:#2

Class写法

如果组件带有state或者方法,就使用Class写法。

1. 引入CSS

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

2. 初始化State

使用ES7句法定义state

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 }

3. 初始化propTypesdefaultProps

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

propTypesdefaultProps的声明应该置顶便于其他开发者阅读。在React v15.3.0版本,推荐使用prop-types这个包替代React.PropTypes。
重要的一点:所有的组件都应当有propTypes验证。

4. 组件内的方法

import React, { Component } from 'react'
import { observer } from 'mobx-react'
import { string, object } from 'prop-types'
import ExpandableForm from './ExpandableForm'
import './styles/ProfileContainer.css'
export default class ProfileContainer extends Component {
  state = { expanded: false }
 
  static propTypes = {
    model: object.isRequired,
    title: string
  }
 
  static defaultProps = {
    model: {
      id: 0
    },
    title: 'Your Name'
  }
  handleSubmit = (e) => {
    e.preventDefault()
    this.props.model.save()
  }
  
  handleNameChange = (e) => {
    this.props.model.changeName(e.target.value)
  }
  
  handleExpand = (e) => {
    e.preventDefault()
    this.setState({ expanded: !this.state.expanded })
  }

在方法中使用箭头函数来替代this.handleExpand.bind(this)

5. this.setState()是异步的。应该使用函数入参

this.setState(prevState => ({ expanded: !prevState.expanded }))

6. 一个组件或者元素含有多个props应当分行写

render() {
    const {
      model,
      title
    } = this.props
    return ( 
      <ExpandableForm 
        onSubmit={this.handleSubmit} 
        expanded={this.state.expanded} 
        onExpand={this.handleExpand}>
        <div>
          <h1>{title}</h1>
          <input
            type="text"
            value={model.name}
            onChange={this.handleNameChange}
            placeholder="Your Name"/>
        </div>
      </ExpandableForm>
    )
  }

7. 避免在子组件中使用闭包

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

8.完整的class组件写法

import React, { Component } from 'react'
import { observer } from 'mobx-react'
import { string, object } from 'prop-types'
// 分开本地导入和依赖导入
import ExpandableForm from './ExpandableForm'
import './styles/ProfileContainer.css'

// 使用修饰器(如果有的话)
@observer
export default class ProfileContainer extends Component {
  state = { expanded: false }
  // 初始化state (ES7) 或者在构造函数(constructor)中初始化state (ES6)
 
  //使用静态属性(ES7)声明propTypes越早越好
  static propTypes = {
    model: object.isRequired,
    title: string
  }

  // 在propTypes后声明defaultProps
  static defaultProps = {
    model: {
      id: 0
    },
    title: 'Your Name'
  }

  // 使用箭头函数绑定指向定义的上下文的this
  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() {
    // 解构props成可读的
    const {
      model,
      title
    } = this.props
    return ( 
      <ExpandableForm 
        onSubmit={this.handleSubmit} 
        expanded={this.state.expanded} 
        onExpand={this.handleExpand}>
        // 如果有2个以上props,分行写
        <div>
          <h1>{title}</h1>
          <input
            type="text"
            value={model.name}
            // onChange={(e) => { model.name = e.target.value }}
            // 避免创造新的闭包,应该使用下面的方法。
            onChange={this.handleNameChange}
            placeholder="Your Name"/>
        </div>
      </ExpandableForm>
    )
  }
}

函数式组件写法

1. propTypes

import React from 'react'
import { observer } from 'mobx-react'
import { func, bool } from 'prop-types'
import './styles/Form.css'
ExpandableForm.propTypes = {
  onSubmit: func.isRequired,
  expanded: bool
}
// Component declaration

2. Destructuring Props and defaultProps

import React from 'react'
import { observer } from 'mobx-react'
import { func, bool } from 'prop-types'
import './styles/Form.css'
ExpandableForm.propTypes = {
  onSubmit: func.isRequired,
  expanded: bool,
  onExpand: func.isRequired
}
function ExpandableForm(props) {
  const formStyle = props.expanded ? {height: 'auto'} : {height: 0}
  return (
    <form style={formStyle} onSubmit={props.onSubmit}>
      {props.children}
      <button onClick={props.onExpand}>Expand</button>
    </form>
  )
}

结合ES6的函数入参解构,可以如下书写

import React from 'react'
import { observer } from 'mobx-react'
import { func, bool } from 'prop-types'
import './styles/Form.css'
ExpandableForm.propTypes = {
  onSubmit: func.isRequired,
  expanded: bool,
  onExpand: func.isRequired
}
function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) {
  const formStyle = expanded ? {height: 'auto'} : {height: 0}
  return (
    <form style={formStyle} onSubmit={onSubmit}>
      {children}
      <button onClick={onExpand}>Expand</button>
    </form>
  )
}

3. 避免箭头函数写法

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

虽然语法没问题,但是这里函数是匿名函数。

4. 高阶组件

import React from 'react'
import { observer } from 'mobx-react'
import { func, bool } from 'prop-types'
import './styles/Form.css'
ExpandableForm.propTypes = {
  onSubmit: func.isRequired,
  expanded: bool,
  onExpand: func.isRequired
}
function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) {
  const formStyle = expanded ? {height: 'auto'} : {height: 0}
  return (
    <form style={formStyle} onSubmit={onSubmit}>
      {children}
      <button onClick={onExpand}>Expand</button>
    </form>
  )
}
export default observer(ExpandableForm)

5. 完整代码

import React from 'react'
import { observer } from 'mobx-react'
import { func, bool } from 'prop-types'
// Separate local imports from dependencies
import './styles/Form.css'

// 在组件前声明propTypes
ExpandableForm.propTypes = {
  onSubmit: func.isRequired,
  expanded: bool,
  onExpand: func.isRequired
}

// 解构props,通过函数入参默认值的方式设定defaultProps
function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) {
  const formStyle = expanded ? { height: 'auto' } : { height: 0 }
  return (
    <form style={formStyle} onSubmit={onSubmit}>
      {children}
      <button onClick={onExpand}>Expand</button>
    </form>
  )
}

// Wrap the component instead of decorating it
export default observer(ExpandableForm)
@YutHelloWorld YutHelloWorld changed the title React Component最佳实践 [译]React Component最佳实践 Aug 16, 2017
@libin-code
Copy link

很有用,感谢分享。

@libin-code
Copy link

来了😏

@Fujitomy
Copy link

对于第三点:3避免箭头函数写法
怎么看到一些网站函数式组件都使用的箭头函数写法
比如下面这个
https://reacttraining.com/react-router/web/example/url-params

const Child = ({ match }) => (

ID: {match.params.id}

);

有什么性能差异吗?

@YutHelloWorld
Copy link
Owner Author

对于第三点:3避免箭头函数写法
怎么看到一些网站函数式组件都使用的箭头函数写法
比如下面这个
https://reacttraining.com/react-router/web/example/url-params

const Child = ({ match }) => (

ID: {match.params.id}

);
有什么性能差异吗?

组件为匿名函数,在调试时,组件可能会显示为<>

@Fujitomy
Copy link

@YutHelloWorld 谢谢,原来是这样

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants