代码结构
class definition
constructor
event handlers
‘component’ lifecycle
getters
render
defaultProps
proptypes
class Person extends React.Component {
constructor (props) {
super(props);
this.state = { smiling: false };
this.handleClick = () => {
this.setState({smiling: !this.state.smiling});
};
}
componentWillMount () {
// add event listeners (Flux Store, WebSocket, document, etc.)
},
componentDidMount () {
// React.getDOMNode()
},
componentWillUnmount () {
// remove event listeners (Flux Store, WebSocket, document, etc.)
},
get smilingMessage () {
return (this.state.smiling) ? "is smiling" : "";
}
render () {
return (
<div onClick={this.handleClick}>
{this.props.name} {this.smilingMessage}
</div>
);
},
}
Person.defaultProps = {
name: 'Guest'
};
Person.propTypes = {
name: React.PropTypes.string
};
格式化属性
当有2个以上属性时,就换行显示
// bad
<Person
firstName="Michael" />
// good
<Person firstName="Michael" />
// bad
<Person firstName="Michael" lastName="Chan" occupation="Designer" favoriteFood="Drunken Noodles" />
// good
<Person
firstName="Michael"
lastName="Chan"
occupation="Designer"
favoriteFood="Drunken Noodles" />
依赖属性
使用getters方法代替定义依赖属性
// bad
firstAndLastName () {
return `${this.props.firstName} ${this.props.lastname}`;
}
// good
get fullName () {
return `${this.props.firstName} ${this.props.lastname}`;
}
依赖状态
使用getters方法代替定义依赖状态,注意为了提高可读性需要在命名时加上一个动词前缀。
// bad
happyAndKnowsIt () {
return this.state.happy && this.state.knowsIt;
}
// good
get isHappyAndKnowsIt () {
return this.state.happy && this.state.knowsIt;
}
这些方法必须返回boolean
类型
用三元运算代替Sub-render方法
保证渲染逻辑都写在render
方法里
// bad
renderSmilingStatement () {
return <strong>{(this.state.isSmiling) ? " is smiling." : ""}</strong>;
},
render () {
return <div>{this.props.name}{this.renderSmilingStatement()}</div>;
}
// good
render () {
return (
<div>
{this.props.name}
{(this.state.smiling)
? <span>is smiling</span>
: null
}
</div>
);
}
视图组件
用定义的组件组成视图。不要创建混杂着布局和功能的一次性组件
// bad
class PeopleWrappedInBSRow extends React.Component {
render () {
return (
<div className="row">
<People people={this.state.people} />
</div>
);
}
}
// good
class BSRow extends React.Component {
render () {
return <div className="row">{this.props.children}</div>;
}
}
class SomeView extends React.Component {
render () {
return (
<BSRow>
<People people={this.state.people} />
</BSRow>
);
}
}
容器组件(有状态组件)
容器组件负责获取数据并交付给相应的子组件渲染,仅此而已。— Jason Bonta
// bad
// CommentList.js
class CommentList extends React.Component {
getInitialState () {
return { comments: [] };
}
componentDidMount () {
$.ajax({
url: "/my-comments.json",
dataType: 'json',
success: function(comments) {
this.setState({comments: comments});
}.bind(this)
});
}
render () {
return (
<ul>
{this.state.comments.map(({body, author}) => {
return <li>{body}—{author}</li>;
})}
</ul>
);
}
}
//good
// CommentList.js
class CommentList extends React.Component {
render() {
return (
<ul>
{this.props.comments.map(({body, author}) => {
return <li>{body}—{author}</li>;
})}
</ul>
);
}
}
// CommentListContainer.js
class CommentListContainer extends React.Component {
getInitialState () {
return { comments: [] }
}
componentDidMount () {
$.ajax({
url: "/my-comments.json",
dataType: 'json',
success: function(comments) {
this.setState({comments: comments});
}.bind(this)
});
}
render () {
return <CommentList comments={this.state.comments} />;
}
}
相关链接:
Container Components
React.js Conf 2015 – Making your app fast with high-performance components
在render
中缓存状态
不要在render
中缓存状态
// bad
render () {
let name = `Mrs. ${this.props.name}`;
return <div>{name}</div>;
}
// good
render () {
return <div>{`Mrs. ${this.props.name}`}</div>;
}
// best
get fancyName () {
return `Mrs. ${this.props.name}`;
}
render () {
return <div>{this.fancyName}</div>;
}
这里多半是出于代码风格的考虑,不过还是保持比较好,我怀疑也可能跟性能有关。
复合条件
不要把复合条件判断放在render
里。
// bad
render () {
return <div>{if (this.state.happy && this.state.knowsIt) { return "Clapping hands" }</div>;
}
// better
get isTotesHappy() {
return this.state.happy && this.state.knowsIt;
},
render() {
return <div>{(this.isTotesHappy) && "Clapping hands"}</div>;
}
这里最好的解决方案是使用容器组件来管理你的状态然后在通过属性(props)往下层传递。
检查空值
不要去检查是否存在某个prop
值,快使用defaultProps
。
// bad
render () {
if (this.props.person) {
return <div>{this.props.person.firstName}</div>;
} else {
return null;
}
}
// good
class MyComponent extends React.Component {
render() {
return <div>{this.props.person.firstName}</div>;
}
}
MyComponent.defaultProps = {
person: {
firstName: 'Guest'
}
};
当你的值是对象或者数组时,使用 PropTypes.shape声明嵌套数据的预期类型。
通过Props设置State
不要通过props值去设置state,除非明显是个初始值。
// bad
getInitialState () {
return {
items: this.props.items
};
}
// good
getInitialState () {
return {
items: this.props.initialItems
};
}
详细请阅读官网的Props in getInitialState Is an Anti-Pattern
命名事件响应方法
// bad
punchABadger () { /*...*/ },
render () {
return <div onClick={this.punchABadger} />;
}
// good
handleClick () { /*...*/ },
render () {
return <div onClick={this.handleClick} />;
}
处理方法的命名必须:
第一个单词为handle
最后一个单词为要响应的事件(比如Click,Change)
现在时态
如果为了避免命名冲突,你可以在handle和事件名中间加入其他信息。比如,你可以定义handleNameChange 和handleAgeChange来区分onChange的不同响应处理。不过当你这样做的时候,你要问问自己是否需要一个新的组件了。
命名事件
可以使用自定义事件替代预设的事件名。
class Owner extends React.Component {
handleDelete () {
// handle Ownee's onDelete event
}
render () {
return <Ownee onDelete={this.handleDelete} />;
}
}
class Ownee extends React.Component {
render () {
return <div onChange={this.props.onDelete} />;
}
}
Ownee.propTypes = {
onDelete: React.PropTypes.func.isRequired
};
使用PropTypes
使用PropTypes可以预先定义属性的类型,可以在之后获得一些有意义的警告信息。
MyValidatedComponent.propTypes = {
name: React.PropTypes.string
};
MyValidatedComponent
的name
属性值如果不是string
类型的话, 会输出警告。
<Person name=1337 />
// Warning: Invalid prop `name` of type `number` supplied to `MyValidatedComponent`, expected `string`.
在这里也可以设置属性是否是必须存在的。
MyValidatedComponent.propTypes = {
name: React.PropTypes.string.isRequired
}
这个组件会验证是否存在name
属性。
<Person />
// Warning: Required prop `name` was not specified in `Person`
相关链接:Prop Validation
使用特殊符号
要在使用React中使用特殊符号,请使用String.fromCharCode()
。
// bad
<div>PiCO · Mascot</div>
// nope
<div>PiCO · Mascot</div>
// good
<div>{'PiCO ' + String.fromCharCode(183) + ' Mascot'}</div>
// better
<div>{`PiCO ${String.fromCharCode(183)} Mascot`}</div>
相关链接:HTML Entities
Tables
浏览器认为你是愚蠢的,但是React不这么人为。请始终为你的table组件添加tbody。
// bad
render () {
return (
<table>
<tr>...</tr>
</table>
);
}
// good
render () {
return (
<table>
<tbody>
<tr>...</tr>
</tbody>
</table>
);
}
浏览器会自动插入tbody当你忘了写它。React则不会,这样会给你的代码带来混乱,请记住使用tbody。
classnames
使用classNames管理你的classes逻辑。
// bad
get classes () {
let classes = ['MyComponent'];
if (this.state.active) {
classes.push('MyComponent--active');
}
return classes.join(' ');
}
render () {
return <div className={this.classes} />;
}
// good
render () {
let classes = {
'MyComponent': true,
'MyComponent--active': this.state.active
};
return <div className={classnames(classes)} />;
}