Airbnb React/JSX 风格指南
这个指南大部分基于现在在JavaScript中流行的标准,尽管有些约定(如: async/await 或 class 的 static 字段)根据具体情况也会被引入或者被禁止。当前这个指南不包括也不推荐任何 ECMAScript stage-3(第三阶段提案) 之前的内容。
React.createElement
, 除非你从一个非 JSX 文件中初始化 app。class extends React.Component
而不是 React.createClass
. eslint: react/prefer-es6-class
react/prefer-stateless-function
// 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 ,最好用正常函数(不是箭头函数)而不是 class:
// 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>; }Why? mixins 会引入一些隐含依赖,导致命名冲突,会导致滚雪球式的复杂度。大多数情况下,mixins 都可以通过组件,高阶组件 HOC或者工具模块更好的实现。
.jsx
作为组件扩展名。ReservationCard.jsx
。react/jsx-pascal-case
// bad import reservationCard from './ReservationCard'; // good import ReservationCard from './ReservationCard'; // bad const ReservationItem = <ReservationCard />; // good const reservationItem = <ReservationCard />;ReservationCard.jsx
应该用 ReservationCard
作为参数名。 然而,对于一个文件夹里的跟组件,应该用 index.jsx
作为文件名,同时用文件夹名作为组件名
// bad import Footer from './Footer/Footer'; // bad import Footer from './Footer/index'; // good import Footer from './Footer';HOC
命名: 用高阶组件名和传入的组件名组合作为生成的组件的 displayName
。 举个例子,一个高阶组件 withFoo()
, 当传入一个组件 Bar
应该生成一个新的组件,他的 displayName
属性是 withFoo(Bar)
。
Why? 组件的 displayName
可以用于开发者工具或者错误信息中,同时还有一个值可以清晰的表达这种组件关系,这可以帮助人们理解到底发生了什么
// 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; }style
、 className
这种属性代表一个明确的意义。 为应用程序的一个子集改变此API会使代码的可读性降低,维护性降低,并可能导致错误。
// bad <MyComponent style="fancy" /> // bad <MyComponent className="fancy" /> // good <MyComponent variant="fancy" />displayName
命名组件。最好通过引用命名组件。
// bad export default React.createClass({ displayName: 'ReservationCard', // stuff goes here }); // good export default class ReservationCard extends React.Component { }react/jsx-closing-bracket-location
react/jsx-closing-tag-location
// bad <Foo superLongParam="bar" anotherSuperLongParam="baz" /> // good <Foo superLongParam="bar" anotherSuperLongParam="baz" /> // 如果能放在一行,也可以用单行表示 <Foo bar="bar" /> // Foo 里面的标签正常缩进 <Foo superLongParam="bar" anotherSuperLongParam="baz" > <Quux /> </Foo> // bad {showButton && <Button /> } // bad { showButton && <Button /> } // good {showButton && ( <Button /> )} // good {showButton && <Button />}"
),但是在js里用单引号('
)。eslint: jsx-quotes
Why? 正常的 HTML 属性也通常使用双引号而不是单引号,所以 JSX 属性也使用这个约定。
// bad <Foo bar='bar' /> // good <Foo bar="bar" /> // bad <Foo style={{ left: "20px" }} /> // good <Foo style={{ left: '20px' }} />no-multi-spaces
, react/jsx-tag-spacing
// bad <Foo/> // very bad <Foo /> // bad <Foo /> // good <Foo />react/jsx-curly-spacing
// bad <Foo bar={ baz } /> // good <Foo bar={baz} />react/jsx-boolean-value
// bad <Foo hidden={true} /> // good <Foo hidden /> // good <Foo hidden /><img>
标签通常会设置 alt
属性。如果图片是表现型的, alt
可以是空字符串或者 <img>
必须有 role="presentation"
这个属性。 eslint: jsx-a11y/alt-text
// 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
属性里用类似 "image", "photo", "picture" 这些单词。 eslint: jsx-a11y/img-redundant-alt
Why? 因为屏幕阅读器已经将 img
发音为图片了,所以这个信息就不需要出现在 alt 文本里了。
// bad <img src="hello.jpg" alt="Picture of me waving hello" /> // good <img src="hello.jpg" alt="Me waving hello" />jsx-a11y/aria-role
// bad - 不是一个 ARIA role <div role="datepicker" /> // bad - 抽象的 ARIA role <div role="range" /> // good <div role="button" />accessKey
。 eslint: `jsx-a11y/no-access-key`Why? 使用屏幕阅读器和键盘的人使用的键盘快捷键和键盘命令之间的不一致使得可访问性变得复杂。
// bad <div accessKey="h" /> // good <div />
key
属性,推荐用稳定的 IDWhy? 不使用稳定杆的 ID is an anti-pattern 会对组件性能产生消极影响,并且组件状态容易出现问题。 如果数组元素可能会发生变化,我们不推荐使用下标作为key。
// bad {todos.map((todo, index) => <Todo {...todo} key={index} /> )} // good {todos.map(todo => ( <Todo {...todo} key={todo.id} /> ))}
Why? propTypes 是一个文档形式,同时提供默认属性意味着使用者不需要假定那么多值。另外,这也意味着你的代码可以忽略类型检查。
// bad function SFC({ foo, bar, children }) { return ¨K0K ; } SFC.propTypes = { foo: PropTypes.number.isRequired, bar: PropTypes.string, children: PropTypes.node, }; // good function SFC({ foo, bar, children }) { return ¨K1K ; } SFC.propTypes = { foo: PropTypes.number.isRequired, bar: PropTypes.string, children: PropTypes.node, }; SFC.defaultProps = { bar: '', children: null, };
{…props}
Why? 除非你更喜欢把不需要的props属性传入组件。而且对于 v15.6.1 及更早以前的 React, 你只能给DOM元素传非HTML属性的props。
例外:
react/no-string-refs
// bad <Foo ref="myRef" /> // good <Foo ref={(ref) => { this.myRef = ref; }} />react/jsx-wrap-multilines
<br />// bad<br />render() {<br /> return ;<br />}</p> <p>// good<br />render() {<br /> return (<br /> <br /> );<br />}</p> <p>// good, 单行可以直接写<br />render() {<br /> const body = </p></li> </ul> ¨K2K ; return <MyComponent>{body}</MyComponent>; }
Tags
Methods
Ordering
`isMounted`
Why? [isMounted
是反模式][anti-pattern], 这个在 ES6 class 里不允许的,而且即将被官方废弃。isMounted
。 eslint: `react/no-is-mounted`propTypes
、 defaultProps
、 contextTypes
等…
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`class extends React.Component
内部属性的顺序:react/jsx-no-bind
Why? render 函数中的绑定调用在每次 render 的时候都会创建一个新的函数。
// 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} />; } }render
函数有返回值。 eslint: react/require-render-return
// bad render() { (<div />); } // good render() { return (<div />); }react/self-closing-comp
// bad <Foo variant="stuff"></Foo> // good <Foo variant="stuff" />react/jsx-closing-bracket-location
// bad <Foo bar="bar" baz="baz" /> // good <Foo bar="bar" baz="baz" />displayName
propTypes
contextTypes
childContextTypes
mixins
statics
defaultProps
getDefaultProps
getInitialState
getChildContext
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
onClickSubmit()
、 onChangeDescription()
getSelectReason()
、 getFooterContent()
renderNavigation()
、 renderProfilePicture()
render
static
方法constructor
getChildContext
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
onClickSubmit()
、 onChangeDescription()
getSelectReason()
、 getFooterContent()
renderNavigation()
、 renderProfilePicture()
render