更合理的编写 React 和 JSX
This style guide is mostly based on the standards that are currently prevalent in JavaScript, although some conventions (i.e async/await or static class fields) may still be included or prohibited on a case-by-case basis. Currently, anything prior to stage 3 is not included nor recommended in this guide.
- 基本规则
- Class vs
React.createClassvs stateless - 混合
- 命名
- 声明
- Alignment
- 引号
- 空格
- Props
- Refs
- Parentheses
- Tags
- Methods
- Ordering
isMounted
- 每个文件仅包含一个React组件。
- 不过,多个无状态组件 是可以的。 eslint:
react/no-multi-comp.
- 不过,多个无状态组件 是可以的。 eslint:
- 总是使用JSX语法。
- 不要使用
React.createElement,除非是从一个非JSX的文件初始化应用。
-
如果你有组件存在内部状态,请使用
class extends React.Component而不是React.createClass。 eslint:react/prefer-es6-classreact/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>; } }
同事如果是一个无状态组件,使用常规函数(非箭头行数)而不是 类:
// bad class Listing extends React.Component { render() { return <div>{this.props.hello}</div>; } } // bad (以来函数名相关的功能将无法使用) const Listing = ({ hello }) => ( <div>{hello}</div> ); // good function Listing({ hello }) { return <div>{hello}</div>; }
为什么?混合引入隐式依赖关系,可能导致名称冲突,并导致代码如滚雪球一样增加复杂性。大多数需要使用混合的场景可以通过组件、高阶组件或实用模块更好地完成。
-
拓展名: 使用
.jsx作为React组件文件的拓展名。 eslint:react/jsx-filename-extension -
文件名: 使用帕斯卡命名法(PascalCase:单词首字母大写)。 例如:
ReservationCard.jsx. -
引用命名: 使用帕斯卡命名法命名React组件,驼峰命名组件实例。 eslint:
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';
-
高阶组件命名: 混合命名高阶组件的名字,同时赋给生成组件的
displayName。 举个例子, 高阶组件withFoo(), 当传入组件Bar将依据这个组件的displayName生成withFoo(Bar)。为什么?一个组件的
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; }
-
属性命名: 避免使用DOM元素的属性名。
Why? People expect props like
styleandclassNameto mean one specific thing. Varying this API for a subset of your app makes the code less readable and less maintainable, and may cause bugs. > 为什么?大家希望属性像style和className来表示特定的事。改变这个API将会降低代码的可读性和维护性。并且容易产生bug。// bad <MyComponent style="fancy" /> // bad <MyComponent className="fancy" /> // good <MyComponent variant="fancy" />
-
Do not use
displayNamefor naming components. Instead, name the component by reference.// bad export default React.createClass({ displayName: 'ReservationCard', // stuff goes here }); // good export default class ReservationCard extends React.Component { }
-
Follow these alignment styles for JSX syntax. eslint:
react/jsx-closing-bracket-locationreact/jsx-closing-tag-location// 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> // bad {showButton && <Button /> } // bad { showButton && <Button /> } // good {showButton && ( <Button /> )} // good {showButton && <Button />}
-
JSX属性使用双引号(
"), JS使用单引号(')。 eslint:jsx-quotes为什么?常规HTML属性通常使用双引号而不是单引号,因此JSX属性遵守这种约定。
// bad <Foo bar='bar' /> // good <Foo bar="bar" /> // bad <Foo style={{ left: "20px" }} /> // good <Foo style={{ left: '20px' }} />
-
自闭合标签前加入一个空格。 eslint:
no-multi-spaces,react/jsx-tag-spacing// bad <Foo/> // very bad <Foo /> // bad <Foo /> // good <Foo />
-
不要在JSX属性语法前后加入空格 eslint:
react/jsx-curly-spacing// bad <Foo bar={ baz } /> // good <Foo bar={baz} />
-
使用规范名字属性。
// bad <Foo UserName="hello" phone_number={12345678} /> // good <Foo userName="hello" phoneNumber={12345678} />
-
省略为
true的属性值。 eslint:react/jsx-boolean-value// bad <Foo hidden={true} /> // good <Foo hidden /> // good <Foo hidden />
-
<img>标签需要有alt。 If the image is presentational,altcan be an empty string or the<img>must haverole="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" />
-
Do not use words like "image", "photo", or "picture" in
<img>altprops. eslint:jsx-a11y/img-redundant-altWhy? Screenreaders already announce
imgelements as images, so there is no need to include this information in the alt text.// bad <img src="hello.jpg" alt="Picture of me waving hello" /> // good <img src="hello.jpg" alt="Me waving hello" />
-
Use only valid, non-abstract ARIA roles. eslint:
jsx-a11y/aria-role// bad - not an ARIA role <div role="datepicker" /> // bad - abstract ARIA role <div role="range" /> // good <div role="button" />
-
Do not use
accessKeyon elements. eslint:jsx-a11y/no-access-key
Why? Inconsistencies between keyboard shortcuts and keyboard commands used by people using screenreaders and keyboards complicate accessibility.
// bad
<div accessKey="h" />
// good
<div />- Avoid using an array index as
keyprop, prefer a stable ID. eslint:react/no-array-index-key
Why? Not using a stable ID is an anti-pattern because it can negatively impact performance and cause issues with component state.
We don’t recommend using indexes for keys if the order of items may change.
// bad
{todos.map((todo, index) =>
<Todo
{...todo}
key={index}
/>
)}
// good
{todos.map(todo => (
<Todo
{...todo}
key={todo.id}
/>
))}- Always define explicit defaultProps for all non-required props.
Why? propTypes are a form of documentation, and providing defaultProps means the reader of your code doesn’t have to assume as much. In addition, it can mean that your code can omit certain type checks.
// 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,
};- Use spread props sparingly.
Why? Otherwise you’re more likely to pass unnecessary props down to components. And for React v15.6.1 and older, you could pass invalid HTML attributes to the DOM.
Exceptions:
- HOCs that proxy down props and hoist propTypes
function HOC(WrappedComponent) {
return class Proxy extends React.Component {
Proxy.propTypes = {
text: PropTypes.string,
isLoading: PropTypes.bool
};
render() {
return <WrappedComponent {...this.props} />
}
}
}- Spreading objects with known, explicit props. This can be particularly useful when testing React components with Mocha’s beforeEach construct.
export default function Foo {
const props = {
text: '',
isPublished: false
}
return (<div {...props} />);
}Notes for use: Filter out unnecessary props when possible. Also, use prop-types-exact to help prevent bugs.
// bad
render() {
const { irrelevantProp, ...relevantProps } = this.props;
return <WrappedComponent {...this.props} />
}
// good
render() {
const { irrelevantProp, ...relevantProps } = this.props;
return <WrappedComponent {...relevantProps} />
}-
Always use ref callbacks. eslint:
react/no-string-refs// bad <Foo ref="myRef" /> // good <Foo ref={(ref) => { this.myRef = ref; }} />
-
Wrap JSX tags in parentheses when they span more than one line. eslint:
react/jsx-wrap-multilines// bad render() { return <MyComponent variant="long body" foo="bar"> <MyChild /> </MyComponent>; } // good render() { return ( <MyComponent variant="long body" foo="bar"> <MyChild /> </MyComponent> ); } // good, when single line render() { const body = <div>hello</div>; return <MyComponent>{body}</MyComponent>; }
-
Always self-close tags that have no children. eslint:
react/self-closing-comp// bad <Foo variant="stuff"></Foo> // good <Foo variant="stuff" />
-
If your component has multi-line properties, close its tag on a new line. eslint:
react/jsx-closing-bracket-location// bad <Foo bar="bar" baz="baz" /> // good <Foo bar="bar" baz="baz" />
-
Use arrow functions to close over local variables.
function ItemList(props) { return ( <ul> {props.items.map((item, index) => ( <Item key={item.key} onClick={() => doSomethingWith(item.name, index)} /> ))} </ul> ); }
-
Bind event handlers for the render method in the constructor. eslint:
react/jsx-no-bindWhy? A bind call in the render path creates a brand new function on every single 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} />; } }
-
Do not use underscore prefix for internal methods of a React component.
Why? Underscore prefixes are sometimes used as a convention in other languages to denote privacy. But, unlike those languages, there is no native support for privacy in JavaScript, everything is public. Regardless of your intentions, adding underscore prefixes to your properties does not actually make them private, and any property (underscore-prefixed or not) should be treated as being public. See issues #1024, and #490 for a more in-depth discussion.
// bad React.createClass({ _onClickSubmit() { // do stuff }, // other stuff }); // good class extends React.Component { onClickSubmit() { // do stuff } // other stuff }
-
Be sure to return a value in your
rendermethods. eslint:react/require-render-return// bad render() { (<div />); } // good render() { return (<div />); }
- Ordering for
class extends React.Component:
- optional
staticmethods constructorgetChildContextcomponentWillMountcomponentDidMountcomponentWillReceivePropsshouldComponentUpdatecomponentWillUpdatecomponentDidUpdatecomponentWillUnmount- clickHandlers or eventHandlers like
onClickSubmit()oronChangeDescription() - getter methods for
renderlikegetSelectReason()orgetFooterContent() - optional render methods like
renderNavigation()orrenderProfilePicture() render
-
How to define
propTypes,defaultProps,contextTypes, etc...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;
-
Ordering for
React.createClass: eslint:react/sort-comp
displayNamepropTypescontextTypeschildContextTypesmixinsstaticsdefaultPropsgetDefaultPropsgetInitialStategetChildContextcomponentWillMountcomponentDidMountcomponentWillReceivePropsshouldComponentUpdatecomponentWillUpdatecomponentDidUpdatecomponentWillUnmount- clickHandlers or eventHandlers like
onClickSubmit()oronChangeDescription() - getter methods for
renderlikegetSelectReason()orgetFooterContent() - optional render methods like
renderNavigation()orrenderProfilePicture() render
- Do not use
isMounted. eslint:react/no-is-mounted
Why?
isMountedis an anti-pattern, is not available when using ES6 classes, and is on its way to being officially deprecated.
This JSX/React style guide is also available in other languages:
Chinese (Simplified): JasonBoy/javascript
Chinese (Traditional): jigsawye/javascript
Español: agrcrobles/javascript
Japanese: mitsuruog/javascript-style-guide
Korean: apple77y/javascript
Polish: pietraszekl/javascript
Portuguese: ronal2do/javascript
Russian: leonidlebedev/javascript-airbnb
Thai: lvarayut/javascript-style-guide
Turkish: alioguzhan/react-style-guide
Ukrainian: ivanzusko/javascript
Vietnam: uetcodecamp/jsx-style-guide