英文原文地址: 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
属性 不能包含转义的引号, 所以当输入 "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 方法中的 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 的顺序:
- 可选的
static
方法
constructor
getChildContext
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
- 点击回调或者事件回调 比如
onClickSubmit()
或者 onChangeDescription()
render
函数中的 getter 方法 比如 getSelectReason()
或者 getFooterContent()
- 可选的 render 方法 比如
renderNavigation()
或者 renderProfilePicture()
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
displayName
propTypes
contextTypes
childContextTypes
mixins
statics
defaultProps
getDefaultProps
getInitialState
getChildContext
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
- 点击回调或者事件回调 比如
onClickSubmit()
or onChangeDescription()
- getter methods for render like
getSelectReason()
or getFooterContent()
- Optional render methods like
renderNavigation()
or renderProfilePicture()
render
isMounted
为什么? isMounted是一种反模式,当使用 ES6 类风格声明 React 组件时该属性不可用,并且即将被官方弃用。