- 2 空格缩进
- UTF-8 编码
明确属于 React 组件
与 JS 文件区分
方便 IDE 的语法高亮
// bad
<Foo UserName="hello" phone_number={12345678} />
// good
<Foo userName="hello" phoneNumber={12345678} />
与文件名保持一致
防止单个文件代码量过大,不好阅读以及管理
与 HTML 属性(双引号)保持一致
// bad
<Foo bar='bar' />
// good
<Foo bar="bar" />
// bad
<Foo style={{left: "20px"}} />
// good
<Foo style={{left: '20px'}} />
// bad
<Foo/>
// good
<Foo />
// bad
render() {
return <MyComponent className="long body" foo="bar">
<MyChild />
</MyComponent>
}
// good
render() {
return (
<MyComponent className="long body" foo="bar">
<MyChild />
</MyComponent>
)
}
// bad
<Foo className="stuff"></Foo>
// good
<Foo className="stuff" />
// bad
<Foo hidden={true} />
// good
<Foo hidden />
getInitialState
getChildContext
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
- 业务逻辑方法
render
以 propTypes
为例
import React, { PropTypes } from 'react';
const Link = React.createClass({
render() {
return <a href={this.props.url} data-id={this.props.id}>{this.props.text}</a>
}
})
Link.propTypes = {
id: PropTypes.number.isRequired,
url: PropTypes.string.isRequired,
text: PropTypes.string,
}
export default Link
场景:获取当前点击的 ID
// bad (JS 和 DOM 之间的通信耗费的资源比较大,既要给元素写数据,又要从元素中读取数据)
const App = React.createClass({
getInitialState: {
return {
items: [{id: 1, name: 'one'}, {id: 2, name: 'two'}]
}
},
handleClick(e) {
console.log(e.target.getAttribute('data-id'))
},
render() {
return (
<ul>
{this.state.items.map(item => {
return <li key={item.id} data-id={item.id} onClick={this.handleClick}>{item.name}</li>
})}
</ul>
)
}
})
// good (利用闭包的特性,避免了和 DOM 通信的过程)
const App = React.createClass({
getInitialState: {
return {
items: [{id: 1, name: 'one'}, {id: 2, name: 'two'}]
}
},
handleClick(i) {
console.log(this.state.items[i].id)
},
render() {
return (
<ul>
{this.state.items.map((item, i) => {
return <li key={item.id} onClick={this.handleClick.bind(this, i)}>{item.name}</li>
})}
</ul>
)
}
})
完整的属性验证既能检查外部传入的数据(不可控因素),减少出错后查找 bug 所耗费的时间,又能让人一目了然了解组件所有对外的接口
// good
import React, { PropTypes } from 'react'
const Comments = React.createClass({
// ...
})
Comments.propTypes = {
items: PropTypes.array.isRequired,
onChange: PropTypes.fun
}
React 从0.14开始引入无状态组件,很多场景组件只关心输入和输出,传入不同的属性,返回不同的 DOM 结构。
This pattern is designed to encourage the creation of these simple components that should comprise large portions of your apps. In the future, we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations.
// bad
const Listing = React.createClass({
render() {
return <div>{this.props.id}</div>
}
})
// bad (arrow functions have none "name" property)
const Listing = ({ id }) => (
<div>{id}</div>
)
// good
function Listing({ id }) {
return <div>{id}</div>;
}
那么无状态组件属性验证如何写?
function Listing({ id }) {
return <div>{id}</div>;
}
Listing.propTypes = {
id: PropTypes.number.isRequired,
}
shouldComponentUpdate
方法在 render
前执行,返回 false
后不再执行 render
。所以可判断新旧状态或属性来决定是否执行 render
shouldComponentUpdate(nextProps, nextState) {
return nextProps.id !== this.props.id
}
// 假如 list 的数量是 10000
// bad
const App = React.createClass({
render() {
return (
<h1>Data List: {this.state.name}</h1>
<ul>{this.state.list.map(item => <li>{item.id}</li>)}</ul>
)
}
})
// good
const List = React.createClass({
shouldComponentUpdate(nextProps) {
return nextProps.list !== this.props.list
},
render() {
return (
<ul>{this.props.list.map(item => <li>{item.id}</li>)}</ul>
)
}
})
const App = React.createClass({
render() {
return (
<h1>Data List: {this.state.name}</h1>
<List list={this.state.list}></List>
)
}
})
如果组件支持 children
,避免使用 shouldComponentUpdate
,否则组件未执行 render
时,children
的状态无法更新
// bad
const Wrap = React.createClass({
shouldComponentUpdate() {
// ...
return false
},
render() {
return <div>{this.props.children}</div>
}
})
const App = React.createClass({
render() {
return (
<Wrap>
<h1>{this.state.title}</h1>
</Wrap>
)
}
})
会导致二次执行
render
直接修改并不会触发
render
// bad
handleClick(id) {
this.state.id = id
}
// good
handleClick(id) {
this.setState({ id })
}
用
refs
来控制状态实际上阻断了数据的流向 https://github.com/facebook/react/blob/master/docs/docs/08.1-more-about-refs.zh-CN.md 除非需要操作 DOM
refs
不能用于无状态函数组件
/**
* bad
* 使用 refs 方式
*/
const Modal = React.createClass({
getInitialState() {
isOpen: false
},
open() {
this.setState({isOpen: true})
},
render() {
return <div className={classnames({open: this.state.isOpen})}>{this.props.children}</div>
}
})
const MyModal = React.createClass({
open() {
this.refs.modal.open()
},
render() {
return (
<Modal ref="modal">
{this.props.children}
<button>确定</button>
</Modal>
)
}
})
const App = React.createClass({
handleOpen() {
this.refs.myModal.open()
},
render() {
return (
<div>
<button onClick="handleOpen">click</button>
<MyModal ref="myModal"></MyModal>
</div>
)
}
})
/**
* good
* 使用数据流控制
*/
function Modal({ open, children }) {
return return <div className={classnames({ open })}>{ children }</div>
}
function MyModal(props) {
return (
<Modal {...props}>
{props.children}
<button>确定</button>
</Modal>
)
}
const App = React.createClass({
handleOpen() {
this.setState({isOpen: true})
},
render() {
return (
<div>
<button onClick="handleOpen">click</button>
<MyModal open={this.state.isOpen}></MyModal>
</div>
)
}
})
React 的事件是虚拟的,事件冒泡不会对元素类型进行判断
// bad
function SearchBox(props) {
return (
<div {...props}>
<input type="text" onChange={props.onChange}>
<button type="button">搜索</button>
</div>
)
}
// onChange 事件冒泡(无视 div 元素不支持 onChange 事件),input 执行一次,加上 div 本身绑定了 onChange,所以执行了两次回调
render(<SearchBox onChange={e => {}} />)
// good
function SearchBox({ onChange, ...other }) {
// SearchBox 本身不再绑定 onChange
return (
<div {...other}>
<input type="text" onChange={onChange}>
<button type="button">搜索</button>
</div>
)
}
render(<SearchBox onChange={e => {}} />)
阻止冒泡的方式来解决也可以
props
传入的状态交给 props
来管理,不要用额外的 state
来保存状态
如果组件的渲染不依赖
props
,又需要管理自己的状态,可用state
。传入props
时优先用props
管理
// bad
const MyInput = React.createClass({
getInitialState() {
return {
value: this.props.value
}
},
componentWillReceiveProps(nextProps) {
'value' in nextProps && this.setState({value: nextProps.value})
},
handleChange(e) {
const value = e.target.value
this.setState({ value })
// 同步外部 props
this.props.onChange(value)
},
render() {
return <input type="text" value={this.state.value} onChange={this.handleChange}>
}
})
// good
const MyInput = React.createClass({
handleChange(e) {
// 同步外部 props
this.props.onChange(e.target.value)
},
render() {
return <input type="text" value={this.props.value} onChange={this.handleChange}>
}
})
复合对象直接写在属性里,每次执行
render
即重新创建一次,同时,如果恰巧 shouldComponentUpdate 内部有相关的判断,则永远不相等
代码规范检查