原文:Our Best Practices for Writing React Components .
这里意译。有些点在之前的文章里提到过:#2
译文地址:https://github.com/YutHelloWo…
如果组件带有state
或者方法,就使用Class写法。
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. 初始化propTypes
和defaultProps
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'
}
propTypes
和defaultProps
的声明应该置顶便于其他开发者阅读。在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)