基本规则
- 每个文件只包含一个 React 组件
- 不过可以包含多个 Stateless 或 Pure 组件。 eslint 规则:react/no-multi-comp
- 使用 JSX 语法
- 除非是从一个非 JSX 文件中初始化 app,否则不要使用 React.createElement
Class vs React.createClass
- 如果需要管理内部状态或 refs,优先使用 class extends React.Component。eslint 规则:react/prefer-es6-class, react/prefer-stateless-function
// bad
const Listing = React.createClass({
render() {
return
}
});
// good
class Listing extends React.Component {
render() {
return
}
}
反之, 则优先使用普通函数(不建议使用箭头函数)。
// bad
class Listing extends React.Component {
render() {
return
}
}
// bad
const Listing = ({ hello }) => (
);
// good
function Listing({ hello }) {
return
}
混淆
-
为什么? Mixins 会增加隐式的依赖,导致命名冲突,并且会以雪球式增加复杂度。在大多数情况下Mixins可以被更好的方法替代,如:组件化,高阶组件,工具模块等。
命名
- 扩展名: 使用 jsx 作为 React 组件的扩展名
- 文件名: 文件命名采用帕斯卡命名法,如:ReservationCard.jsx
- 引用名: 组件引用采用帕斯卡命名法,其实例采用驼峰式命名法。eslint rules: react/jsx-pascal-case
// bad
const reservationCard = require(‘./ReservationCard’);
// good
const ReservationCard = require(‘./ReservationCard’);
// bad
const ReservationItem =
// good
const reservationItem =
- 组件命名: 使用文件名作为组件名。例如:ReservationCard.jsx 组件的引用名应该是 ReservationCard。然而,对于一个目录的根组件,应该使用 index.jsx 作为文件名,使用目录名作为组件名。
// bad
const Footer = require(‘./Footer/Footer.jsx’)
// bad
const Footer = require(‘./Footer/index.jsx’)
// good
const Footer = require(‘./Footer’)
高阶组件命名: 如果是新生成的模块,其模块名的 displayName 应该为高阶模块名和传入模块名的组合. 例如, 高阶模块 withFoo(), 当传入一个 Bar 模块的时候, 新的模块名 displayName 应该为 withFoo(Bar).
为什么?一个模块的 displayName 可能会在开发者工具或者错误信息中使用到,因此有一个能清楚的表达这层关系的值能帮助我们更好的理解模块发生了什么,更好的Debug.
// bad
export default function withFoo(WrappedComponent) {
return function WithFoo(props) {
return
}
}
// good
export default function withFoo(WrappedComponent) {
function WithFoo(props) {
return
}
const wrappedComponentName = WrappedComponent.displayName
|| WrappedComponent.name
|| ‘Component’;
WithFoo.displayName = withFoo(${wrappedComponentName})
;
return WithFoo;
}
属性命名: 避免使用 DOM 属性为组件的属性命名.
为什么?对于 style 和 className 这样的属性名会默认代表一些含义,在你的应用中使用这些属性来表示其他的含义会使你的代码更难阅读,更难维护,并且可能会引起bug。
// bad
// bad
// good
声明
- 不要通过 displayName 来命名组件,通过引用来命名组件
// bad
export default React.createClass({
displayName: ‘ReservationCard’,
// stuff goes here
});
// good
export default class ReservationCard extends React.Component {
}
对齐
- 对于 JSX 语法,遵循下面的对齐风格。eslint rules: react/jsx-closing-bracket-location,
react/jsx-closing-tag-location
// bad
// good
anotherSuperLongParam=”baz”
/>
// if props fit in one line then keep it on the same line
// children get indented normally
anotherSuperLongParam=”baz”
>
引号
- 对于 JSX 使用双引号,对其它所有 JS 属性使用单引号。eslint:jsx-quotes
为什么?因为 JSX 属性不能包含被转移的引号,并且双引号使得如 “don’t” 一样的连接词很容易被输入。常规的 HTML 属性也应该使用双引号而不是单引号,JSX 属性反映了这个约定。
// bad
// good
// bad
// good
空格
- 在自闭和标签之前留一个空格。eslint: no-multi-spaces, react/jsx-tag-spacing
// bad
// very bad
// bad
// good
- 不要在JSX {} 引用括号里两边加空格. eslint: react/jsx-curly-spacing
// bad
// good
属性
- 属性名采用驼峰式命名法
// bad
phone_number={12345678}
/>
// good
phoneNumber={12345678}
/>
- 属性值为 true,可以忽略赋值。eslint: react/jsx-boolean-value
// bad
/>
// good
/>
// good
- img 标签要添加 alt 属性或者 role=”presentation”。 eslint: jsx-a11y/alt-text
// bad
// good
// good
// good
不要在 img 标签的 alt 属性中使用 “image”, “photo”, 或 “picture” 一类的单词。eslint:jsx-a11y/img-redundant-alt
为什么? 屏幕助读器已经将 img 作为图片了,所以没必要再在 alt 属性中进行说明
// bad
// good
- 只是用正确且具体的 ARIA 属性。eslint:jsx-a11y/aria-role
// bad - not an ARIA role
// bad - abstract ARIA role
// good
不要在元素上使用 accessKey 属性。eslint:jsx-a11y/no-access-key
为什么?屏幕助读器在键盘快捷键与键盘命令时造成的不统一性会导致阅读性更加复杂。
// bad
// good
- 避免使用数组的 index 来作为属性 key 的值,推荐使用唯一ID(为什么?)
// bad
{todos.map((todo, index) =>
key={index}
/>
)}
// good
{todos.map(todo => (
key={todo.id}
/>
))}
对于组件所有的非必要属性需在 defaultProps 中定义。
为什么?propTypes 也是一种文档形式,提供 defaultProps 定义更有利于其他人阅读你的代码,并且能省略一些类型检查
// bad
function SFC({ foo, bar, children }) {
return
}
SFC.propTypes = {
foo: PropTypes.number.isRequired,
bar: PropTypes.string,
children: PropTypes.node,
};
// good
function SFC({ foo, bar, children }) {
return
}
SFC.propTypes = {
foo: PropTypes.number.isRequired,
bar: PropTypes.string,
children: PropTypes.node,
};
SFC.defaultProps = {
bar: ‘’,
children: null,
};
- 尽量少用扩展运算符
为什么?除非你很想传递一些不必要的属性。对于React v15.6.1和更早的版本,你可以给DOM传递一些无效的HTML属性
例外情况:
- 使用了变量提升的高阶组件
function HOC(WrappedComponent) {
return class Proxy extends React.Component {
Proxy.propTypes = {
text: PropTypes.string,
isLoading: PropTypes.bool
};
render() {
return
}
}
}
- 很清楚扩展运算符是用于对象时。在使用 Mocha 测试组件的时扩展运算符就非常有用
export default function Foo {
const props = {
text: ‘’,
isPublished: false
}
return (
);}
注意:使用时要尽可能过滤不必要的属性。使用 prop-types-exact能预防 bug。
//good
render() {
const { irrelevantProp, …relevantProps } = this.props;
return
}
//bad
render() {
const { irrelevantProp, …relevantProps } = this.props;
return
}
Refs
- 使用 ref callbacks。eslint:react/no-string-refs
// bad
/>
// good
/>
括号
- 当组件跨行时,要用括号包裹 JSX 标签。eslint: react/wrap-multilines
/// bad
render() {
return
}
// good
render() {
return (
);
}
// good, when single line
render() {
const body =
return
}
标签
- 没有子组件的父组件使用自闭和标签。eslint: react/self-closing-comp
// bad
// good
- 如果组件有多行属性,闭合标签应写在新的一行上。eslint: react/jsx-closing-bracket-location
// bad
baz=”baz” />
// good
baz=”baz”
/>
方法
- 使用箭头函数来访问本地变量
function ItemList(props) {
return (
- key={item.key}
onClick={() => doSomethingWith(item.name, index)}
/>
))}
{props.items.map((item, index) => (
);
}
- 在构造函数中绑定需要在 render 方法使用的事件处理函数(绑定 this)。eslint:react/jsx-no-bind
为什么?在组件每次 render 时, 每次 bind 调用都会创建新的函数
// bad
class extends React.Component {
onClickDiv() {
// do stuff
}
render() {
return
}
}
// good
class extends React.Component {
constructor(props) {
super(props);
this.onClickDiv = this.onClickDiv.bind(this);
}
onClickDiv() {
// do stuff
}
render() {
return
}
}
- 不要对 React 组件的内置方法使用 underscore 前缀
为什么?_ 前缀在某些语言中通常被用来表示私有变量或者函数,但在原生 JavaScript 不支持所谓的私有变量,所有的变量函数都是共有的。在变量名之前加上下划线并不会使这些变量私有化,并且所有的属性都应该被视为是共有的。相关 issue:#1024 / #490 。
// bad
React.createClass({
_onClickSubmit() {
// do stuff
}
// other stuff
});
// good
class extends React.Component {
onClickSubmit() {
// do stuff
}
// other stuff
});
- 确保 render 方法中有返回值。eslint: react/require-render-return
// bad
render() {
(
}
// good
render() {
return (
}
顺序
- 继承 React.Component 的类的方法遵循下面的顺序
- constructor
- optional static methods
- getChildContext
- componentWillMount
- componentDidMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- componentDidUpdate
- componentWillUnmount
- clickHandlers or eventHandlers like onClickSubmit() or onChangeDescription()
- getter methods for render like getSelectReason() or getFooterContent()
- Optional render methods like renderNavigation() or renderProfilePicture()
- render
- 怎么定义 propTypes,defaultProps,contextTypes 等等…
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 {this.props.text}
}
}
Link.propTypes = propTypes;
Link.defaultProps = defaultProps;
export default Link;
- 使用 React.createClass 时,方法顺序如下:
- displayName
- propTypes
- contextTypes
- childContextTypes
- mixins
- statics
- defaultProps
- getDefaultProps
- getInitialState
- getChildContext
- componentWillMount
- componentDidMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- componentDidUpdate
- componentWillUnmount
- clickHandlers or eventHandlers like onClickSubmit() or onChangeDescription()
- getter methods for render like getSelectReason() or getFooterContent()
- Optional render methods like renderNavigation() or renderProfilePicture()
- render
eslint: react/sort-comp