--- group: Coding Style order: 5 --- # React Coding Specification ## 1 编码风格 ### 1.1 缩进 - 1.1.1 `mandatory` JSX 语法使用 2 个空格缩进。eslint: [react/jsx-indent](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent.md) [react/jsx-indent-props](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent-props.md) [react/jsx-closing-tag-location](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-tag-location.md) 对于 JSX 语法,遵循与 JS 规约和 HTML 规约一致的 2 个空格缩进,不要使用 4 空格或 tab 缩进: ```jsx static // bad // good ``` ### 1.2 空格 - 1.2.1 `mandatory` 自闭合标签的斜线前有且仅有一个空格。eslint: [no-multi-spaces](https://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 static // bad // very bad // bad // good ``` - 1.2.2 `mandatory` JSX 行内属性之间仅有一个空格。eslint: [react/jsx-props-no-multi-spaces](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-props-no-multi-spaces.md) 同一行中标签和属性之间、属性之间只有一个空格。 ```jsx static // bad // good ``` - 1.2.3 `mandatory` JSX 属性的大括号内部两侧无空格。eslint: [react/jsx-curly-spacing](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md) ```jsx static // bad // good ``` - 1.2.4 `mandatory` 不要在 JSX 属性的等号两边加空格。eslint: [jsx-equals-spacing](https://eslint.org/docs/rules/jsx-equals-spacing) ```jsx static // bad ; // good ; ``` ### 1.3 引号 - 1.3.1 `mandatory` JSX 属性使用双引号,不要使用单引号。eslint: [jsx-quotes](https://eslint.org/docs/rules/jsx-quotes) 为什么?HTML 属性通常使用双引号而不是单引号,因此 JSX 属性沿用了这种约定。 其他 JS 使用单引号。 ```jsx static // bad // good // bad // good ``` ### 1.4 小括号 - 1.4.1 `mandatory` 多行的 JSX 标签需用小括号包裹。eslint: [react/jsx-wrap-multilines](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-wrap-multilines.md) ```jsx static // bad render() { return ; } // good render() { return ( ); } // good - 单行的 jsx 无需加圆括号 render() { const body =
hello
; return {body}; } ``` ### 1.5 标签 - 1.5.1 `mandatory` 无子元素的标签需写成自闭合标签。eslint: [react/self-closing-comp](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md) ```jsx static // bad // good ``` - 1.5.2 `mandatory` 标签属性的换行。eslint: [react/jsx-max-props-per-line](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-max-props-per-line.md) [react/jsx-first-prop-new-line](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-first-prop-new-line.md) 对 JSX 标签属性的换行,遵循以下规则: - 标签名和它的属性可以写在一行,前提是不超过单行最大 100 字符数的限制 - 如果标签有多个属性,且存在换行,则每个属性都需要换行独占一行 ```jsx static // bad - 属性应全部换行,或全部跟组件名写在一行 // good // good - 组件名和属性可以写在一行,前提是不超过单行最大字符限制 // bad // good ``` - 1.5.3 `mandatory` 标签的属性有多行时,结束标签需另起一行。eslint: [react/jsx-closing-bracket-location](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md) ```jsx static // bad // good ``` - 1.5.4 `mandatory` 禁止在有子节点的组件或 DOM 元素中使用 dangerouslySetInnerHTML 属性。eslint: [react/no-danger-with-children](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger-with-children.md) ```jsx static // bad
Children
Children // good
Children
Children ``` - 1.5.5 `mandatory` HTML 自闭标签不能有子节点。eslint: [react/void-dom-elements-no-children](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/void-dom-elements-no-children.md) HTML 自闭标签,比如 img,br,hr,被统称为空 DOM 元素,不能给他们定义子节点。 ```jsx static // bad
Children

// good
Children
``` - 1.5.6 `recommended` 不要使用危险属性。eslint: [react/no-danger](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger.md) React中的危险属性是指那些已知会引起应用程序漏洞的属性。这些属性命名为 `dangerouslyXyz` 已经清楚地表明它们是危险的,应该尽量避免使用。[详细文档](https://facebook.github.io/react/tips/dangerously-set-inner-html.html) ```jsx static // bad
; // good
Hello World
; ``` - 1.5.7 `mandatory` JSX 语句的文本节点中不要使用注释字符串(例如,以//或/ *开头)。eslint: [react/jsx-no-comment-textnodes](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-comment-textnodes.md) ```jsx static // bad class Hello extends React.Component { render() { return (
// empty div
); } }; class Hello extends React.Component { render() { return (
/* empty div */
); } }; // good class Hello extends React.Component { render() { return
{/* empty div */}
; } }; class Hello extends React.Component { render() { return
; } }; class Hello extends React.Component { render() { return
; } }; ``` - 1.5.8 `mandatory` 标签中禁止出现无意义字符,比如 > " } '。eslint: [react/no-unescaped-entities](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unescaped-entities.md) `>` 可用 `>` 替代 `"` 可用 `"`,`“` , `" ` 或者 `”` 替代 `'` 可用 `'`,`‘`,`'` 或者 `’` 替代 `}` 可用 `}` 替代 或者写在表达式里,比如 `
{'>'}
` ```jsx static // bad {/* oops! */} c="d" Intended body text // good
>
{'>'}
``` ## 2 语言特性 ### 2.1 基本 - 2.1.1 `referenced` 使用 `JSX` 语法时,防止 `React` 变量被标记为未使用,可以使用 `@jsx` 标注来指定 `React` 之外的变量。eslint: [react/jsx-uses-react](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md) ```jsx static // bad var React = require('react'); // nothing to do with React /** @jsx Foo */ var React = require('react'); var Hello =
Hello {this.props.name}
; // good var React = require('react'); var Hello =
Hello {this.props.name}
; /** @jsx Foo */ var Foo = require('foo'); var Hello =
Hello {this.props.name}
; ``` - 2.1.2 `mandatory` 不要使用未声明的组件。eslint: [react/jsx-no-undef](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md) [react/jsx-uses-vars](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md) 不允许没有引用组件就直接使用,也可能是组件名拼写错误。 ```jsx static // bad ; // good import Hello from './Hello'; ; ``` - 2.1.3 `mandatory` 每个文件只包含一个 React 组件。eslint: [react/no-multi-comp](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md) 但是可以包含多个[函数组件](https://reactjs.org/docs/components-and-props.html#function-and-class-components)。 - 2.1.4 `mandatory` 不要在函数组件中使用 this。eslint: [react/no-this-in-sfc](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-this-in-sfc.md) ```jsx static // bad function Foo(props, context) { return (
{this.context.foo ? this.props.bar : ''}
); } // good function Foo(props, context) { return (
{context.foo ? props.bar : ''}
); } ``` - 2.1.5 `mandatory` 使用 ES6 class 创建组件 ,而不是 [createReactClass](https://reactjs.org/docs/react-without-es6.html) 。eslint: [react/prefer-es6-class](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md) ```jsx static // bad const Listing = createReactClass({ // ... render() { return
{this.state.hello}
; } }); // good class Listing extends React.Component { // ... render() { return
{this.state.hello}
; } } ``` - 2.1.6 `referenced` 如果组件没有内部状态或 refs ,应使用函数组件,而不是类组件。eslint: [react/prefer-stateless-function](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md) ```jsx static // bad class Listing extends React.Component { render() { return
{this.props.hello}
; } } // bad const Listing = ({ hello }) => (
{hello}
); // good function Listing({ hello }) { return
{hello}
; } ``` - 2.1.7 `mandatory` 不要使用 React.createElement,除非你不是用 JSX 文件初始化应用程序。 ### 2.2 方法 - 2.2.1 `recommended` 不要在 JSX 属性中使用 .bind()。eslint: [react/jsx-no-bind](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md) 这不利于组件性能,每次 render 都会创建一个新的函数。 有 2 种替代方案: - 在 `constructor` 中绑定事件处理函数 - 使用 react 的 [property initializers](https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding) 特性或 [ES7 autobind decorator ](https://www.npmjs.com/package/core-decorators#autobind) ```jsx static // bad class extends React.Component { onClickDiv() { // ... } render() { return
; } } // good - 在 constructor 中绑定事件处理函数 class extends React.Component { constructor(props) { super(props); this.onClickDiv = this.onClickDiv.bind(this); } onClickDiv() { // ... } render() { return
; } } // good - 使用 react 的 property initializers 特性 class extends React.Component { constructor(props) { super(props); } onClickDiv = () => { // ... } render() { return
; } } ``` - 2.2.2 `mandatory` render 方法必须要有返回值。eslint: [react/require-render-return](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-render-return.md) ```jsx static // bad render() { (
); } // good render() { return (
); } ``` - 2.2.3 `mandatory` 禁止使用 ReactDOM.render 的返回值。eslint: [react/no-render-return-value](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-render-return-value.md) render()返回 ReactComponent 实例的引用。然而,应该避免使用这个返回值,因为在某些情况下,React 的未来版本中 render 方法可能会异步执行。如果需要引用 ReactComponent 实例,根元素需要增加 ref 回调。 ```jsx static // bad const inst = ReactDOM.render(, document.body); doSomethingWithInst(inst); // good ReactDOM.render(, document.body); ReactDOM.render(, document.body, doSomethingWithInst); ``` - 2.2.4 `mandatory` 在扩展 React.PureComponent 时禁止使用 shouldComponentUpdate。eslint: [react/no-redundant-should-component-update](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-redundant-should-component-update.md) 定义 React.PureComponent 扩展组件时使用 shouldComponentUpdate 虽然有效,但是扩展 PureComponent 变得毫无意义。 ```jsx static // bad class Foo extends React.PureComponent { shouldComponentUpdate() { // do check } render() { return
Radical!
} } function Bar() { return class Baz extends React.PureComponent { shouldComponentUpdate() { // do check } render() { return
Groovy!
} } } // good class Foo extends React.Component { shouldComponentUpdate() { // do check } render() { return
Radical!
} } function Bar() { return class Baz extends React.Component { shouldComponentUpdate() { // do check } render() { return
Groovy!
} } } class Qux extends React.PureComponent { render() { return
Tubular!
} } ``` - 2.2.5 `mandatory` 禁止使用已经废弃的方法。eslint: [react/no-deprecated](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-deprecated.md) 随着React版本升级,有些方法逐渐被弃用。 ```jsx static // bad React.render(, root); React.unmountComponentAtNode(root); React.findDOMNode(this.refs.foo); React.renderToString(); React.renderToStaticMarkup(); React.createClass({ /* Class object */ }); const propTypes = { foo: PropTypes.bar, }; //Any factories under React.DOM React.DOM.div(); import React, { PropTypes } from 'react'; class Foo extends React.Component { componentWillMount() { } componentWillReceiveProps() { } componentWillUpdate() { } // ... } class Foo extends React.PureComponent { componentWillMount() { } componentWillReceiveProps() { } componentWillUpdate() { } // ... } var Foo = createReactClass({ componentWillMount: function() {}, componentWillReceiveProps: function() {}, componentWillUpdate: function() {}, // ... }) // good ReactDOM.render(, root); // When [1, {"react": "0.13.0"}] ReactDOM.findDOMNode(this.refs.foo); import { PropTypes } from 'prop-types'; class Foo { componentWillMount() { } componentWillReceiveProps() { } componentWillUpdate() { } } ``` - 2.2.6 `mandatory` 不要使用 findDOMNode。eslint: [react/no-find-dom-node](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-find-dom-node.md) [严格模式下已经弃用 findDOMNode](https://reactjs.org/docs/strict-mode.html#warning-about-deprecated-finddomnode-usage)。 ```jsx static // bad class MyComponent extends Component { componentDidMount() { findDOMNode(this).scrollIntoView(); } render() { return
} } // good class MyComponent extends Component { componentDidMount() { this.node.scrollIntoView(); } render() { return
this.node = node} /> } } ``` - 2.2.7 `mandatory` 不要使用 componentWillMount、componentWillReceiveProps、componentWillUpdate。 不要再使用 [componentWillMount](https://reactjs.org/docs/react-component.html#unsafe_componentwillmount) 、[componentWillReceiveProps](https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops)、[componentWillUpdate](https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate)。使用这些生命周期方法通常会导致错误和不一致,因此React 计划在17版本删掉这些方法。 - componentWillMount() 可以用 constructor() 或 componentDidMount() 替代; - componentWillReceiveProps() 可以用 componentDidUpdate() 或其他方式替换; - componentWillUpdate() 可以用 componentDidUpdate() 替换或者把逻辑写在 getSnapshotBeforeUpdate() 中。 使用[rename-unsafe-lifecycles codemod](https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles)自动为不推荐使用的生命周期钩子添加“UNSAFE_”前缀。转化为 - UNSAFE_componentWillMount() - UNSAFE_componentWillReceiveProps() - UNSAFE_componentWillUpdate() - 2.2.8 `mandatory` 不要在 componentWillUpdate 内改变 state 值。eslint: [react/no-will-update-set-state](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-will-update-set-state.md) 首先,不要再使用 componentWillUpdate,[React 未来在17版本计划删掉 componentWillUpdate](https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate)。通常可以用 componentDidUpdate() 替代。使用[rename-unsafe-lifecycles codemod](https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles)自动更新组件。 不要在 componentWillUpdate 调用 this.setState()。若你需要更新状态响应属性的变更,使用 [getDerivedStateFromProps()](https://react.docschina.org/docs/react-component.html#static-getderivedstatefromprops) 代替。在 componentWillUpdate 中改变 state 的值可能会引起组件的不确定状态。 ```jsx static // bad class Hello extends React.Component { componentWillUpdate() { this.setState({ name: this.props.name.toUpperCase() }); } render() { return
Hello {this.state.name}
; } }; // good class Hello extends React.Component { componentWillUpdate() { this.props.prepareHandler(); } render() { return
Hello {this.props.name}
; } }; class Hello extends React.Component { componentWillUpdate() { this.prepareHandler(function callback(newName) { this.setState({ name: newName }); }); } render() { return
Hello {this.props.name}
; } }; ``` ### 2.3 Props - 2.3.1 `mandatory` 采用小驼峰风格命名 prop 。eslint: [react/no-unknown-property](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md) ```jsx static // bad // good ``` - 2.3.2 `mandatory` 声明的 prop 必须被使用。eslint: [react/no-unused-prop-types](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unused-prop-types.md) 声明而未使用的 prop 可能带来潜在的问题,也会给维护者造成困扰,应将它们删除。 ```jsx static // bad var Hello = createReactClass({ propTypes: { name: PropTypes.string }, render: function() { return
Hello Bob
; } }); var Hello = createReactClass({ propTypes: { firstname: PropTypes.string.isRequired, middlename: PropTypes.string.isRequired, // middlename is never used below lastname: PropTypes.string.isRequired }, render: function() { return
Hello {this.props.firstname} {this.props.lastname}
; } }); // good var Hello = createReactClass({ propTypes: { name: PropTypes.string }, render: function() { return
Hello {this.props.name}
; } }); ``` - 2.3.3 `referenced` props,state 优先使用解构赋值。eslint: [react/destructuring-assignment](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/destructuring-assignment.md) ```jsx static // bad const MyComponent = (props) => { return (
) }; // good const MyComponent = ({id}) => { return (
) }; const MyComponent = (props, context) => { const { id } = props; return (
) }; ``` - 2.3.4 `mandatory` prop 值为 true 时,可以省略它的值。eslint: [react/jsx-boolean-value](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md) ```jsx static // bad