# react

# 基本规则

# Class vs React.createClass vs stateless

// 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>;
  }
}
成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

如果你没有使用 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>;
}
成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Mixins

Why? mixins 会引入一些隐含依赖,导致命名冲突,会导致滚雪球式的复杂度。大多数情况下,mixins 都可以通过组件,高阶组件 HOC (opens new window)或者工具模块更好的实现。

# 对齐

  • 对 JSX 语法使用这些对齐风格。 eslint: react/jsx-closing-bracket-location (opens new window) react/jsx-closing-tag-location (opens new window)

    // 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 />}
    
    成功
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39

# 引号

  • 在 JSX 属性中用双引号("),但是在js里用单引号(')。eslint: jsx-quotes (opens new window)

    Why? 正常的 HTML 属性也通常使用双引号而不是单引号,所以 JSX 属性也使用这个约定。

    // bad
    <Foo bar='bar' />
    
    // good
    <Foo bar="bar" />
    
    // bad
    <Foo style={{ left: "20px" }} />
    
    // good
    <Foo style={{ left: '20px' }} />
    
    成功
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 空格

# Props

  • props 用小驼峰

    // bad
    <Foo
      UserName="hello"
      phone_number={12345678}
    />
    
    // good
    <Foo
      userName="hello"
      phoneNumber={12345678}
    />
    
    成功
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • 如果 prop 的值是 true 可以忽略这个值,直接写 prop 名就可以。 eslint: react/jsx-boolean-value (opens new window)

    // bad
    <Foo
      hidden={true}
    />
    
    // good
    <Foo
      hidden
    />
    
    // good
    <Foo hidden />
    
    成功
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  • <img> 标签通常会设置 alt 属性。如果图片是表现型的, alt可以是空字符串或者 <img> 必须有 role="presentation" 这个属性。 eslint: jsx-a11y/alt-text (opens new window)

    // 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" />
    
    成功
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • 不要在 <img>alt 属性里用类似 "image", "photo", "picture" 这些单词。 eslint: jsx-a11y/img-redundant-alt (opens new window)

    Why? 因为屏幕阅读器已经将 img 发音为图片了,所以这个信息就不需要出现在 alt 文本里了。

    // bad
    <img src="hello.jpg" alt="Picture of me waving hello" />
    
    // good
    <img src="hello.jpg" alt="Me waving hello" />
    
    成功
    1
    2
    3
    4
    5
  • 只用可用的,不抽象的 ARIA roles (opens new window). eslint: jsx-a11y/aria-role (opens new window)

    // bad - 不是一个 ARIA role
    <div role="datepicker" />
    
    // bad - 抽象的 ARIA role
    <div role="range" />
    
    // good
    <div role="button" />
    
    成功
    1
    2
    3
    4
    5
    6
    7
    8
  • 不要在元素上用 accessKey。 eslint: jsx-a11y/no-access-key (opens new window)

Why? 使用屏幕阅读器和键盘的人使用的键盘快捷键和键盘命令之间的不一致使得可访问性变得复杂。

// bad
<div accessKey="h" />

// good
<div />
成功
1
2
3
4
5
  • 避免用数组下标作为 key 属性,推荐用稳定的 ID

Why? 不使用稳定杆的 ID is an anti-pattern (opens new window) 会对组件性能产生消极影响,并且组件状态容易出现问题。 如果数组元素可能会发生变化,我们不推荐使用下标作为key。

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

// good
{todos.map(todo => (
  <Todo
    {...todo}
    key={todo.id}
  />
))}
成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 对于所有非必须属性,定义一个明确的默认值。

Why? propTypes 是一个文档形式,同时提供默认属性意味着使用者不需要假定那么多值。另外,这也意味着你的代码可以忽略类型检查。

// 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,
};
成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  • 少用props扩展运算符,既 {...props}

Why? 除非你更喜欢把不需要的props属性传入组件。而且对于 v15.6.1 及更早以前的 React, 你只能给DOM元素传非HTML属性的props (opens new window)

例外:

  • HOC 是代理 props 并且提成了propTypes
function HOC(WrappedComponent) {
  return class Proxy extends React.Component {
    Proxy.propTypes = {
      text: PropTypes.string,
      isLoading: PropTypes.bool
    };

    render() {
      return <WrappedComponent {...this.props} />
    }
  }
}
成功
1
2
3
4
5
6
7
8
9
10
11
12
  • 扩展一个已知的,有明确属性的对象也是可以的。这个对用 Mocha 的 beforeEach 函数做单测时尤其有用。
export default function Foo {
  const props = {
    text: '',
    isPublished: false
  }

  return (<div {...props} />);
}
成功
1
2
3
4
5
6
7
8

使用说明: 尽可能过滤出不需要的属性。同时用prop-type-exact (opens new window)去帮助避免bug。

// bad
render() {
  const { irrelevantProp, ...relevantProps  } = this.props;
  return <WrappedComponent {...this.props} />
}

// good
render() {
  const { irrelevantProp, ...relevantProps  } = this.props;
  return <WrappedComponent {...relevantProps} />
}
成功
1
2
3
4
5
6
7
8
9
10
11

# Refs

Why? react可以更优雅的完成对组件销毁时的变量回收,你只要这么用,react在销毁组件时,this.myRef 很方便的就可以被清理变为null。

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

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

# 括号

  • 当 JSX 标签有多行时,用圆括号包起来。eslint: react/jsx-wrap-multilines (opens new window)

    // bad
    render() {
      return <MyComponent variant="long body" foo="bar">
               <MyChild />
             </MyComponent>;
    }
    
    // good
    render() {
      return (
        <MyComponent variant="long body" foo="bar">
          <MyChild />
        </MyComponent>
      );
    }
    
    // good, 单行可以直接写
    render() {
      const body = <div>hello</div>;
      return <MyComponent>{body}</MyComponent>;
    }
    
    成功
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

# 标签

# Methods

  • 用箭头函数关闭局部变量。

    function ItemList(props) {
      return (
        <ul>
          {props.items.map((item, index) => (
            <Item
              key={item.key}
              onClick={() => doSomethingWith(item.name, index)}
            />
          ))}
        </ul>
      );
    }
    
    成功
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  • 在构造函数里绑定事件处理函数。 eslint: react/jsx-no-bind (opens new window)

    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} />;
      }
    }
    
    成功
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
  • 不要在 React 组件里使用下划线作为内部方法名前缀。

    Why? 下划线前缀有时候在其他语言里被用于表示私有。但是 JavaScript 原生并不支持私有,所有东西都是公有的。尽管在你的意图里,对你的属性添加下划线前缀不是真的是他变成私有属性,而且任何属性(不论是不是下划线前缀)都被认为是公有的。详细讨论见问题#1024 (opens new window),和#490 (opens new window)

    // bad
    React.createClass({
      _onClickSubmit() {
        // do stuff
      },
    
      // other stuff
    });
    
    // good
    class extends React.Component {
      onClickSubmit() {
        // do stuff
      }
    
      // other stuff
    }
    
    成功
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  • 确保你的 render 函数有返回值。 eslint: react/require-render-return (opens new window)

    // bad
    render() {
      (<div />);
    }
    
    // good
    render() {
      return (<div />);
    }
    
    成功
    1
    2
    3
    4
    5
    6
    7
    8
    9

# Ordering

  • class extends React.Component 内部属性的顺序:
  1. 可选的 static 方法
  2. constructor
  3. getChildContext
  4. componentWillMount
  5. componentDidMount
  6. componentWillReceiveProps
  7. shouldComponentUpdate
  8. componentWillUpdate
  9. componentDidUpdate
  10. componentWillUnmount
  11. clickHandlers or eventHandlers 如: onClickSubmit()onChangeDescription()
  12. getter methods for render 如: getSelectReason()getFooterContent()
  13. optional render methods 如: renderNavigation()renderProfilePicture()
  14. render
  • 如何定义 propTypesdefaultPropscontextTypes 等...

    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;
    
    成功
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
  • React.createClass 内部属性排序: eslint: react/sort-comp (opens new window)

  1. displayName
  2. propTypes
  3. contextTypes
  4. childContextTypes
  5. mixins
  6. statics
  7. defaultProps
  8. getDefaultProps
  9. getInitialState
  10. getChildContext
  11. componentWillMount
  12. componentDidMount
  13. componentWillReceiveProps
  14. shouldComponentUpdate
  15. componentWillUpdate
  16. componentDidUpdate
  17. componentWillUnmount
  18. clickHandlers or eventHandlers 如: onClickSubmit()onChangeDescription()
  19. getter methods for render 如: getSelectReason()getFooterContent()
  20. optional render methods 如: renderNavigation()renderProfilePicture()
  21. render

# isMounted

Why? [isMounted 是反模式][anti-pattern], 这个在 ES6 class 里不允许的,而且即将被官方废弃。