Airbnb React 编码规范

英文原文地址: Airbnb React/JSX Style Guide

Airbnb React/JSX Style Guide

用更合理的方式书写 React 和 JSX

基本规则

  • 一个文件内只包含一个 React 组件。

  • 总是使用 JSX 语法。

  • 不要使用 React.createElement,除非你从一个不是 JSX 的文件初始化你的应用。

Class vs React.createClass vs stateless

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// bad
const Listing = React.createClass({
// ...
render() {
return <div>{this.state.hello}</div>;
}
});
// good
class Listing extends React.Component {
// ...
render() {
return <div>{this.state.hello}</div>;
}
}
  • 如果没有内部 state 或者 refs,那么普通函数 (非箭头函数) 比类的写法更好:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// bad
class Listing extends React.Component {
render() {
return <div>{this.props.hello}</div>;
}
}
// bad (since arrow functions do not have a "name" property)
const Listing = ({ hello }) => (
<div>{hello}</div>
);
// good
function Listing({ hello }) {
return <div>{hello}</div>;
}

命名

  • 扩展名:React 组件使用.jsx扩展名
  • 文件名:文件名使用帕斯卡命名。 例如: ReservationCard.jsx
  • 引用命名:React 组件使用帕斯卡命名,引用实例采用骆驼命名。 eslint: react/jsx-pascal-case
1
2
3
4
5
6
7
8
9
10
11
// bad
import reservationCard from './ReservationCard';
// good
import ReservationCard from './ReservationCard';
// bad
const ReservationItem = <ReservationCard />;
// good
const reservationItem = <ReservationCard />;

命名

  • 扩展: 使用 .jsx React 组件的扩展名。
  • 文件名: 为文件使用帕斯卡命名方式(PascalCase)。 例如: ReservationCard.jsx
  • 引用命名:为 React组件 使用帕斯卡命名方式(PascalCase),为他们的实例使用驼峰方式命名(camelCase)。eslint: react/jsx-pascal-case
1
2
3
4
5
6
7
8
9
10
11
// bad
import reservationCard from './ReservationCard';
// good
import ReservationCard from './ReservationCard';
// bad
const ReservationItem = <ReservationCard />;
// good
const reservationItem = <ReservationCard />;
  • 组件命名:组件名称应该和文件名一致。例如: ReservationCard.jsx 应该有一个 ReservationCard 的引用名称。 然而,如果是在目录中的组件, 应该使用 index.jsx 作为文件名并且使用目录名称作为组件名:
1
2
3
4
5
6
7
8
// bad
import Footer from './Footer/Footer';
// bad
import Footer from './Footer/index';
// good
import Footer from './Footer';

声明

  • 不要使用 displayName 属性来命名组件,应该使用类的引用名称。
1
2
3
4
5
6
7
8
9
// bad
export default React.createClass({
displayName: 'ReservationCard',
// stuff goes here
});
// good
export default class ReservationCard extends React.Component {
}

对齐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// bad
<Foo superLongParam="bar"
anotherSuperLongParam="baz" />
// good
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
/>
// if props fit in one line then keep it on the same line
<Foo bar="bar" />
// children get indented normally
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
>
<Quux />
</Foo>

引号

  • JSX 的属性都采用双引号("),其他的 JS 都使用单引号。eslint: jsx-quotes

为什么这样做?JSX 属性 不能包含转义的引号, 所以当输入 "don't" 这类的缩写的时候用双引号会更方便。标准的 HTML 属性通常也会使用双引号替代单引号,所以 JSX 属性也会遵守这样的约定。

1
2
3
4
5
6
7
8
9
10
11
// bad
<Foo bar='bar' />
// good
<Foo bar="bar" />
// bad
<Foo style={{ left: "20px" }} />
// good
<Foo style={{ left: '20px' }} />

空格

  • 总是在你的自闭标签内包含一个空格。
1
2
3
4
5
6
7
8
9
10
11
12
// bad
<Foo/>
// very bad
<Foo />
// bad
<Foo
/>
// good
<Foo />

属性

  • 总是为你的属性名使用驼峰命名(camelCase)。
1
2
3
4
5
6
7
8
9
10
11
// bad
<Foo
UserName="hello"
phone_number={12345678}
/>
// good
<Foo
userName="hello"
phoneNumber={12345678}
/>
1
2
3
4
5
6
7
8
9
// bad
<Foo
hidden={true}
/>
// good
<Foo
hidden
/>

大括号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// bad
render() {
return <MyComponent className="long body" foo="bar">
<MyChild />
</MyComponent>;
}
// good
render() {
return (
<MyComponent className="long body" foo="bar">
<MyChild />
</MyComponent>
);
}
// good, when single line
render() {
const body = <div>hello</div>;
return <MyComponent>{body}</MyComponent>;
}

标签

1
2
3
4
5
// bad
<Foo className="stuff"></Foo>
// good
<Foo className="stuff" />
1
2
3
4
5
6
7
8
9
10
// bad
<Foo
bar="bar"
baz="baz" />
// good
<Foo
bar="bar"
baz="baz"
/>

方法

  • 使用箭头函数关闭本地变量。
1
2
3
4
5
6
7
8
9
10
11
12
function ItemList(props) {
return (
<ul>
{props.items.map((item, index) => (
<Item
key={item.key}
onClick={() => doSomethingWith(item.name, index)}
/>
))}
</ul>
);
}
  • 为 render 方法的处理事件在构造函数中进行绑定。 eslint: react/jsx-no-bind

为什么这样做? 在 render 方法中的 bind 调用每次调用 render 的时候都会创建一个全新的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// bad
class extends React.Component {
onClickDiv() {
// do stuff
}
render() {
return <div onClick={this.onClickDiv.bind(this)} />
}
}
// good
class extends React.Component {
constructor(props) {
super(props);
this.onClickDiv = this.onClickDiv.bind(this);
}
onClickDiv() {
// do stuff
}
render() {
return <div onClick={this.onClickDiv} />
}
}
  • 不要使用下划线前缀为 React 组件的内部方法命名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// bad
React.createClass({
_onClickSubmit() {
// do stuff
},
// other stuff
});
// good
class extends React.Component {
onClickSubmit() {
// do stuff
}
// other stuff
}

排序

  • class extends React.Component 的顺序:
  1. 可选的 static 方法
  2. constructor
  3. getChildContext
  4. componentWillMount
  5. componentDidMount
  6. componentWillReceiveProps
  7. shouldComponentUpdate
  8. componentWillUpdate
  9. componentDidUpdate
  10. componentWillUnmount
  11. 点击回调或者事件回调 比如 onClickSubmit() 或者 onChangeDescription()
  12. render 函数中的 getter 方法 比如 getSelectReason() 或者 getFooterContent()
  13. 可选的 render 方法 比如 renderNavigation() 或者 renderProfilePicture()
  14. render
  • 怎样定义 propTypes, defaultProps, contextTypes等……
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React, { PropTypes } from 'react';
const propTypes = {
id: PropTypes.number.isRequired,
url: PropTypes.string.isRequired,
text: PropTypes.string,
};
const defaultProps = {
text: 'Hello World',
};
class Link extends React.Component {
static methodsAreOk() {
return true;
}
render() {
return <a href={this.props.url} data-id={this.props.id}>{this.props.text}</a>
}
}
Link.propTypes = propTypes;
Link.defaultProps = defaultProps;
export default Link;

React.createClass的排序:eslint: react/sort-comp

  1. displayName
  2. propTypes
  3. contextTypes
  4. childContextTypes
  5. mixins
  6. statics
  7. defaultProps
  8. getDefaultProps
  9. getInitialState
  10. getChildContext
  11. componentWillMount
  12. componentDidMount
  13. componentWillReceiveProps
  14. shouldComponentUpdate
  15. componentWillUpdate
  16. componentDidUpdate
  17. componentWillUnmount
  18. 点击回调或者事件回调 比如 onClickSubmit() or onChangeDescription()
  19. getter methods for render like getSelectReason() or getFooterContent()
  20. Optional render methods like renderNavigation()or renderProfilePicture()
  21. render

isMounted

为什么? isMounted是一种反模式,当使用 ES6 类风格声明 React 组件时该属性不可用,并且即将被官方弃用。