A-A+

# Airbnb React/JSX 样式指南

2017年07月22日 JavaScript 暂无评论
# Airbnb React/JSX 样式指南
* 书写 React and JSX 的一个合理的方法*

## 目录
1. [基本规则](#basic-rules)
1. [Class vs `React.createClass` vs无状态的](#class-vs-reactcreateclass-vs-stateless)
1. [Mixins](#mixins)
1. [命名](#naming)
1. [声明](#declaration)
1. [对齐方式](#alignment)
1. [引号](#quotes)
1. [空格](#spacing)
1. [Props](#props)
1. [Refs](#refs)
1. [圆括号](#parentheses)
1. [标签](#tags)
1. [方法](#methods)
1. [Ordering](#ordering)
1. [`isMounted`](#ismounted)
## 
基本规则
- 每个文件仅包含一个 React 组件
-然而, 单个文件允许多个 [无状态, or Pure, Components](https://facebook.github.io/react/docs/reusable-components.html#stateless-functions) . eslint: [`react/no-multi-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md#ignorestateless).
- 总是使用 JSX 语法.
- 不用 `React.createElement` 除非你从非jsx文件中初始化appyou're initializing the app from a file that is not JSX.
## Class vs `React.createClass` vs stateless
- 若有内部状态和引用(refs),用`class extends React.Component` 而非`React.createClass`. eslint: [`react/prefer-es6-class`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md) [`react/prefer-stateless-function`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md)
```jsx
// 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>;
}
}
```
若无状态和refs,使用普通函数(非箭头函数)而非class
And if you don't have state or refs, prefer normal functions (not arrow functions) over classes:
```jsx
// bad
class Listing extends React.Component {
render() {
return <div>{this.props.hello}</div>;
}
}
// bad (relying on function name inference is discouraged)
const Listing = ({ hello }) => (
<div>{hello}</div>
);
// good
function Listing({ hello }) {
return <div>{hello}</div>;
}
```
## Mixins
- [不使用 mixins](https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html).
> Why? Mixins introduce implicit dependencies, cause name clashes, and cause snowballing complexity. Most use cases for mixins can be accomplished in better ways via components, higher-order components, or utility modules.
##命名
- **扩展**: 为  React 组件 使用`.jsx` 扩展.
- **文件名**: 使用“帕斯卡拼写法”.例如., `ReservationCard.jsx`.
- **引用命名**:  React 组件使用“ 帕斯卡拼写法 ”. 其实例使用驼峰命名法。 eslint: [`react/jsx-pascal-case`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md)
```jsx
// bad
import reservationCard from './ReservationCard';
// good
import ReservationCard from './ReservationCard';
// bad
const ReservationItem = <ReservationCard />;
// good
const reservationItem = <ReservationCard />;
```
- **组件命名**: 使用文件名作为组件名,例如, `ReservationCard.jsx` 的引用名应该是`ReservationCard`.然而, f对于目录的根组件, 应该使用`index.jsx`作为文件名以及目录名作为组件名称:
```jsx
// bad
import Footer from './Footer/Footer';
// bad
import Footer from './Footer/index';
// good
import Footer from './Footer';
```
- **高阶组件命名**: 在生成的组件上使用高阶组件的名称和传入的组件名称作为 `displayName` . 例如, 高阶组件 `withFoo()`, 当传入组件 `Bar` 应该产生一个有   `withFoo(Bar)`. 的`displayName` 。
>为什么?一个组件的 `displayName` 可被开发工具使用或在错误信息里面,有明确表达的这种关系的价值帮助人们理解发生了什么。
```jsx
// bad
export default function withFoo(WrappedComponent) {
return function WithFoo(props) {
return <WrappedComponent {...props} foo />;
}
}
// good
export default function withFoo(WrappedComponent) {
function WithFoo(props) {
return <WrappedComponent {...props} foo />;
}
const wrappedComponentName = WrappedComponent.displayName
|| WrappedComponent.name
|| 'Component';
WithFoo.displayName = `withFoo(${wrappedComponentName})`;
return WithFoo;
}
```
- **Props命名**:避免因不同的目的使用DOM组件 prop名
>为何? 人民期望 props 像`style` 和`className` 意味着一个专用的东西. 改变你app子集的API会使代码的可读性和可维护性降低,可能会导致bug。
```jsx
// bad
<MyComponent style="fancy" />
// good
<MyComponent variant="fancy" />
```
## 声明
- 不使用`displayName` 命名组件,相反.通过引用命名组件
```jsx
// bad
export default React.createClass({
displayName: 'ReservationCard',
// stuff goes here
});
// good
export default class ReservationCard extends React.Component {
}
```
## 对齐
- 遵循这些 JSX 对齐语法. eslint: [`react/jsx-closing-bracket-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md)
```jsx
// bad
<Foo superLongParam="bar"
anotherSuperLongParam="baz" />
// good
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
/>
//若props 适合一行就保持一行
<Foo bar="bar" />
// 子节点通常缩进
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
>
<Quux />
</Foo>
```
##引号
- 总使用双引号(`"`) 为JSX属性, 但为其他的js使用单引号 (`'`) . eslint: [`jsx-quotes`](http://eslint.org/docs/rules/jsx-quotes)
>为何? 常规的HTML 属性也通成使用双引号代替单引号,故JSX 属性照此惯例。
```jsx
// bad
<Foo bar='bar' />
// good
<Foo bar="bar" />
// bad
<Foo style={{ left: "20px" }} />
// good
<Foo style={{ left: '20px' }} />
```
##空格
- 总在自闭和标签中使用一个空格. eslint: [`no-multi-spaces`](http://eslint.org/docs/rules/no-multi-spaces), [`react/jsx-tag-spacing`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-tag-spacing.md)
```jsx
// bad
<Foo/>
// very bad
<Foo />
// bad
<Foo
/>
// good
<Foo />
```
- 不要在JSX大括号中加上空格. eslint: [`react/jsx-curly-spacing`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md)
```jsx
// bad
<Foo bar={ baz } />
// good
<Foo bar={baz} />
```
## Props
- 总使用驼峰命名法。
```jsx
// bad
<Foo
UserName="hello"
phone_number={12345678}
/>
// good
<Foo
userName="hello"
phoneNumber={12345678}
/>
```
- 当值明显为`true`时,忽略其prop值. eslint: [`react/jsx-boolean-value`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md)
```jsx
// bad
<Foo
hidden={true}
/>
// good
<Foo
hidden
/>
```
- 总为   `<img>` 标签包含`alt` prop 。若图片是可显示的, `alt` 也可是个空字符串,或者 `<img>` 必须有 `role="presentation"`. eslint: [`jsx-a11y/alt-text`](https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/alt-text.md)
```jsx
// bad
<img src="hello.jpg" />
// good
<img src="hello.jpg" alt="Me waving hello" />
// good
<img src="hello.jpg" alt="" />
// good
<img src="hello.jpg" role="presentation" />
```
- 不在`<img>` `alt` props使用如 "image", "photo", 或"picture" . eslint: [`jsx-a11y/img-redundant-alt`](https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/img-redundant-alt.md)
>为何? 屏幕阅读器已经把`img`元素作为图像,因此不需要在alt文本中包含此信息
```jsx
// bad
<img src="hello.jpg" alt="Picture of me waving hello" />
// good
<img src="hello.jpg" alt="Me waving hello" />
```
- 仅使用有效的,非抽象API [ARIA roles](https://www.w3.org/TR/wai-aria/roles#role_definitions). eslint: [`jsx-a11y/aria-role`](https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/aria-role.md)
```jsx
// bad - not an ARIA role
<div role="datepicker" />
// bad - abstract ARIA role
<div role="range" />
// good
<div role="button" />
```
- 不在元素上使用 `accessKey`. eslint: [`jsx-a11y/no-access-key`](https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-access-key.md)
>为何?使用屏幕阅读器和键盘的人使用的键盘快捷键和键盘命令之间的不一致使可访问性复杂化。
```jsx
// bad
<div accessKey="h" />
// good
<div />
```
- 避免使用数组索引作为key` prop, 使用唯一的 ID更好. ([why?](https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318))
```jsx
// bad
{todos.map((todo, index) =>
<Todo
{...todo}
key={index}
/>
)}
// good
{todos.map(todo => (
<Todo
{...todo}
key={todo.id}
/>
))}
```
始终为所有非必需的props定义明确的defaultprops。
>为什么?prop类型是一种文档形式,提供defaultProps意味着代码的渲染不需要做额外的假设。此外,它也意味着代码可以省略某些类型的检查。
```jsx
// bad
function SFC({ foo, bar, children }) {
return <div>{foo}{bar}{children}</div>;
}
SFC.propTypes = {
foo: PropTypes.number.isRequired,
bar: PropTypes.string,
children: PropTypes.node,
};
// good
function SFC({ foo, bar, children }) {
return <div>{foo}{bar}{children}</div>;
}
SFC.propTypes = {
foo: PropTypes.number.isRequired,
bar: PropTypes.string,
children: PropTypes.node,
};
SFC.defaultProps = {
bar: '',
children: null,
};
```
## Refs
- 总使用ref回调. eslint: [`react/no-string-refs`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md)
```jsx
// bad
<Foo
ref="myRef"
/>
// good
<Foo
ref={(ref) => { this.myRef = ref; }}
/>
```
## 圆括号
-用圆括号包裹跨越多行的JSX标签
 eslint: [`react/jsx-wrap-multilines`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-wrap-multilines.md)
```jsx
// bad
render() {
return <MyComponent className="long body" foo="bar">
<MyChild />
</MyComponent>;
}
// good
render() {
return (
<MyComponent className="long body" foo="bar">
<MyChild />
</MyComponent>
);
}
// good,单行
render() {
const body = <div>hello</div>;
return <MyComponent>{body}</MyComponent>;
}
```
##标签
-始终自动关闭没有子节点的标签
. eslint: [`react/self-closing-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md)
```jsx
// bad
<Foo className="stuff"></Foo>
// good
<Foo className="stuff" />
```
- 如果组件具有多行属性,则在新的一行关闭其标签. eslint: [`react/jsx-closing-bracket-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md)
```jsx
// bad
<Foo
bar="bar"
baz="baz" />
// good
<Foo
bar="bar"
baz="baz"
/>
```
##方法
-使用箭头函数关闭局部变量
.
```jsx
function ItemList(props) {
return (
<ul>
{props.items.map((item, index) => (
<Item
key={item.key}
onClick={() => doSomethingWith(item.name, index)}
/>
))}
</ul>
);
}
```
-为构造函数中的渲染方法绑定事件处理程序
. eslint: [`react/jsx-no-bind`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md)
为什么?渲染路径中的绑定调用在每次渲染时都会创建一个全新的函数。
```jsx
// 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组件的内部方法使用下划线前缀。
>为什么?下划线前缀有时在其他语言中表示隐藏文件。但是,与那些语言不同的是,在JavaScript中没有对隐藏的本地支持,一切都是公开的。不管你的目的为何,添加下划线前缀属性实际上并没有使他们私有的,任何属性(下划线前缀或不)都应该被视为公共的。
 参见 issues [#1024](https://github.com/airbnb/javascript/issues/1024), 和 [#490](https://github.com/airbnb/javascript/issues/490) 了解更多信息。.
```jsx
// bad
React.createClass({
_onClickSubmit() {
// do stuff
},
// other stuff
});
// good
class extends React.Component {
onClickSubmit() {
// do stuff
}
// other stuff
}
```
- 确保你的 `render`方法有返回值. eslint: [`react/require-render-return`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-render-return.md)
```jsx
// bad
render() {
(<div />);
}
// good
render() {
return (<div />);
}
```
## 顺序
-  `class extends React.Component`: 的顺序
1. 可选的`static`方法
1. `constructor`
1. `getChildContext`
1. `componentWillMount`
1. `componentDidMount`
1. `componentWillReceiveProps`
1. `shouldComponentUpdate`
1. `componentWillUpdate`
1. `componentDidUpdate`
1. `componentWillUnmount`
1. *clickHandlers or eventHandlers* like `onClickSubmit()` or `onChangeDescription()`
1. *getter methods for `render`* like `getSelectReason()` or `getFooterContent()`
1. *optional render methods* like `renderNavigation()` or `renderProfilePicture()`
1. `render`
- 如何定义 `propTypes`, `defaultProps`, `contextTypes`, 等...
```jsx
import React from 'react';
import PropTypes from 'prop-types';
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`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md)
1. `displayName`
1. `propTypes`
1. `contextTypes`
1. `childContextTypes`
1. `mixins`
1. `statics`
1. `defaultProps`
1. `getDefaultProps`
1. `getInitialState`
1. `getChildContext`
1. `componentWillMount`
1. `componentDidMount`
1. `componentWillReceiveProps`
1. `shouldComponentUpdate`
1. `componentWillUpdate`
1. `componentDidUpdate`
1. `componentWillUnmount`
1. *clickHandlers or eventHandlers* like `onClickSubmit()` or `onChangeDescription()`
1. *getter methods for `render`* like `getSelectReason()` or `getFooterContent()`
1. *optional render methods* like `renderNavigation()` or `renderProfilePicture()`
1. `render`
## `isMounted`
- 不使用 `isMounted`. eslint: [`react/no-is-mounted`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md)
> Why? [`isMounted` is an anti-pattern][anti-pattern], is not available when using ES6 classes, and is on its way to being officially deprecated.
[anti-pattern]: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html
- ![中文版](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese (Simplified)**: [JasonBoy/javascript](https://github.com/JasonBoy/javascript/tree/master/react)