超级面板
文章目录
最新文章
最近更新
文章分类
标签列表
文章归档

React 编码规范整理

基础规则

每个文件只应包含一个有状态的组件。
每个文件只包含一个组件,如有需要,该文件还可以包含多个无状态的纯组件。

使用JSX 书写组件,如非必要,禁止使用 React.createElement

类组件

State

不要直接修改 state
除了 state 初始化外,其它地方修改 state,需要使用 setState() 方法,直接赋值 state 不会触发组件的渲染。

// bad
this.setState({comment: 'Hello'});

// good
this.state.comment = 'hello';

Methods 函数

当在 render() 里使用事件处理方法时,提前在构造函数里把 this 绑定上去,或者使用箭头函数。

// 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} />;
}
}

// good
class extends React.Component {
onClickDiv: () => {
// do stuff
}

render() {
return <div onClick={this.onClickDiv} />;
}
}

在React模块中,不要给所谓的私有函数添加 _ 前缀,如果在 typescript 可以使用 private 关键字。

// bad
React.createClass({
_onClickSubmit() {
// do stuff
},

// other stuff
});

// good typescript
class extends React.Component {
private onClickSubmit() {
// do stuff
}

// other stuff
}

render 方法中总是确保 return 有返回值.

// bad
render() {
(<div />);
}

// good
render() {
return (<div />);
}

异步请求

通常在 componentDidMount 这个生命周期函数中发起异步请求。

componentDidMount() {
loadData().then(res => {
this.setState({ data: res.data });
});
}

计算属性/复合属性

如果有用到复杂的计算属性,可以使用 getter,不要使用计算函数:

// bad
getComputedValue() {
return this.state.value1 + this.state.value2;
}

//good
get computedValue() {
return this.state.value1 + this.state.value2;
}

渲染函数

复杂的条件逻辑,使用一个函数渲染,使用函数渲染时需要以 render 开头。

// bad
{someConditional
? (
<Bar
superLongParam="bar"
anotherSuperLongParam="baz"
/>
)
: (
anotherOneConditional
? (
<Tee
superLongParam="bar"
anotherSuperLongParam="baz"
/>
)
: (
<Bee
superLongParam="bar"
anotherSuperLongParam="baz"
/>
)
)
}

// good
renderText() {
if (someConditional) {
return (
<Bar
superLongParam="bar"
anotherSuperLongParam="baz"
/>
);
}
if (anotherOneConditional) {
return (
<Tee
superLongParam="bar"
anotherSuperLongParam="baz"
/>
);
}
return (
<Bee
superLongParam="bar"
anotherSuperLongParam="baz"
/>
);
}

禁止使用 componentWillMount、 componentWillReceiveProps 和 componentWillUpdate

React v16 废弃了这三个方法,具体原因参考:Update on Async Rendering

禁止使用 Mixins

Mixins Considered Harmful

组件属性书写排序

  • class extends React.Component 的生命周期函数:
  • 可选的 static 方法
  • private 属性
  • constructor 构造函数
  • getterget selectReason()get footerContent()
  • getChildContext 获取子元素内容
  • componentDidMount 模块渲染后
  • shouldComponentUpdate 判断模块需不需要重新渲染
  • componentDidUpdate 模块渲染结束
  • componentWillUnmount 模块将从DOM中清除, 做一些清理任务
  • 点击回调或者事件处理器onClickSubmit()onChangeDescription()
  • 可选的 render 方法renderNavigation()renderProfilePicture()
  • render render() 方法

如何定义 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;

命名规范

文件名

使用大驼峰命名法,如 ReservationCard.jsx

// bad
import reservationCard from './ReservationCard';

// good
import ReservationCard from './ReservationCard';

如果整个目录是一个组件, 使用 index.js 作为文件名, import 的时候忽略文件名:

// bad
import Footer from './Footer/Footer';

// bad
import Footer from './Footer/index';

// good
import Footer from './Footer';

扩展名

如果文件内是一个 React 组件,则文件后缀名使用 jsx,如果引入了 typescript 使用 tsx

// bad
ReservationCard.js

// good
ReservationCard.jsx

// good
ReservationCard.tsx

组件命名

使用大驼峰命名法,一般和文件名保持一致,避免使用 displayName 而是使用类名。

// bad
export default class extends React.Component {
displayName: 'ReservationCard',
// stuff goes here
}

// good
export default class ReservationCard extends React.Component {}

高阶组件命名

生成一个新的组件时, 新组件的组件名 displayName 应该为高阶组件名和传入的组件名的组合. 例如, 高阶组件 withFoo(), 当传入一个 Bar 组件的时候, 生成的组件名 displayName 应该为 withFoo(Bar)

// 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;
}

Why? A component’s displayName may be used by developer tools or in error messages, and having a value that clearly expresses this relationship helps people understand what is happening.

组件的引用名

组件的实例应该使用小驼峰法命名。

// bad
const ReservationItem = <ReservationCard />;

// good
const reservationItem = <ReservationCard />;

组件的属性名

JSX属性名使用小驼峰法命名。

// bad
<Foo
UserName="hello"
phone_number={12345678}
/>

// good
<Foo
userName="hello"
phoneNumber={12345678}
/>

组件的事件监听函数

事件监听函数使用 onhandle 开头的小驼峰命名法。

// bad
doclick() {}

// bad
afterChange() {}

// good
handleClick() {}

// good
onClick() {}

// good
handleAfterChange() {}

// good
onChanged() {}

渲染函数

如果一个函数返回值为要渲染的JSX 时,需要以 render 开头。

// bad
getText() {
return (
<Bar
superLongParam="bar"
anotherSuperLongParam="baz"
/>
);
}

// good
renderText() {
return (
<Bar
superLongParam="bar"
anotherSuperLongParam="baz"
/>
);
}

空格、缩进和对齐

在自关闭的标签前加一个空格。

// bad
<Foo/>

// very bad
<Foo />

// bad
<Foo
/>

// good
<Foo />

属性能在一行中完全显示, 则可以直接写成一行,如果有多行属性的话, 新建一行编写属性,并新建一行关闭标签。

// 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" />

子组件要缩进两个空格

<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
>
<Quux />
</Foo>

组件在表达式中换行时,需要使用括号包裹起来

// bad
{
showButton &&
<Button />
}

// good
{showButton && (
<Button />
)}

// good
{showButton && <Button />}

// good
{someReallyLongConditional
&& anotherLongConditional
&& (
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
/>
)
}

// good
{someConditional ? (
<Foo />
) : (
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
/>
)}

对于没有子元素的标签来说总是自己关闭标签。

// bad
<Foo className="stuff"></Foo>

// good
<Foo className="stuff" />

Props 属性

属性值

如果属性值为 true, 可以直接省略。

// bad
<Foo hidden={true} />

// good
<Foo hidden />

key

避免使用数组的 index 来作为 key 属性的值。

应当使用稳定不变的 ID。(使用不稳定的 ID 会降低性能、造成组件状态出错) 。特别是当元素的顺序可能改变的情况下,不应使用数组的 index 作为 key.

// bad
{todos.map((todo, index) =>
<Todo
{...todo}
key={index}
/>
)}

// good
{todos.map(todo => (
<Todo
{...todo}
key={todo.id}
/>
))}

defaultProps

对于所有非必须的属性,总是定义defaultProps属性.

// 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,
};

Refs

总是在Refs里使用回调函数

// bad
<Foo ref="myRef" />

// good
<Foo
ref={(ref) => { this.myRef = ref; }}
/>

说明

本规范主要参考 airbnb 的标准,并结合以前的 React 开发经验和 React 的版本迭代改变做了大量修改整理而成。