# # ES5&ES6 写法对照表(react)

#### # 来源:

React on ES6+
React/React Native 的 ES5 ES6 写法对照表

## # class 定义语法

> 值得注意的是，我们已经删除了两个括号和一个后缀分号，而对于每个声明的方法，我们都省略了一个冒号，一个 function 关键字和一个逗号。

    class Photo extends React.Component {
        render() {
            return <img alt={this.props.caption} src={this.props.src} />
        }
    }
    // The ES5 way
    var Photo = React.createClass({
        handleDoubleTap: function(e) { ... }
        render: function() { ... }
    })
    //ThE ES6 way
    class Photo extends React.Component {
        handleDoubleTap(e) { ... }
        render() { ... }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

## # 生命周期方法

> 所有的生命周期方法，但可以定义为使用新的类语法期望的。该课程 constructor 现在承担以前填写的角色 componentWillMount：

    // es5
    var EmbedModal = React.createClass({
        componentWillMount: function() { ... }
    })
    // es6
    class EmbedModal extends React.createClass (
        construtor(props) {
            super(props);
            // Operations usually carried out in componentWillMount go here
        }
    )

1
2
3
4
5
6
7
8
9
10
11

## # 属性初始化

> ES7 属性初始化器在类的构造函数中运行，其中 this 指的是正在构建的类的实例，所以初始状态仍然可以依赖于 this.props。值得注意的是，我们不再需要根据吸气功能定义道具默认值和初始状态对象。

    // es5
    var Video = React.createClass({
      getDefaultProps: function() {
        return {
          autoPlay: flase,
          maxLoops: 10,
        };
      },
      getInitalState: function() {
        return {
          loopsRemaining: this.props.maxloops,
        };
      },
      propsTypes: {
        autoPlay: React.PropsTypes.bool.isRequired,
        maxloops: React.PropsTypes.number.isRequired,
        posterFrameSrc: React.PropsTypes.string.isRequired,
        videoSrc: React.PropsTypes.string.isRequired,
      },
    });
    // es6
    class Video extends React.Component {
      static defaultProps = {
        autoPlay: false,
        maxloops: 10,
      };
      static propsTypes = {
        autoPlay: React.PropsTypes.bool.isRequired,
        maxloops: React.propsTypes.number.isRequired,
        posterFrameSrc: React.PropsTypes.string.isRequired,
        videoSrc: React.PropsTypes.string.isRequired,
      };
      state = {
        loopsRemaining: this.props.maxloops,
      };
    }

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

## # 箭头函数说明

> 该 React.createClass 方法用于对组件的实例方法执行一些额外的绑定工作，以确保其中的 this 关键字将引用该组件的实例。

    // 自动绑定React.createClass
    var PostInfo = React.createClass({
      handleOptionsButtonClick: function(e) {
        // 这里，'this' 是指组件实例。
        this.setState({ showOptionModal: true });
      },
    });

1
2
3
4
5
6
7

> 由于我们 React.createClass 在使用 ES6 +类语法定义组件时不涉及该方法，所以我们需要手动绑定实例方法，无论我们想要这种行为：

    // 手动绑定，无论你是否需要
    class PostInfo extends React.Component {
      construtor(props) {
        super(props);
        // 手动将此方法绑定到组件实例中…
        this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
      }
      handleOptionsButtonClick(e) {
        // …为了确保'this'指的是组件实例。
        this.setState({ showOptionsModal: true });
      }
    }

1
2
3
4
5
6
7
8
9
10
11
12

> 幸运的是，通过组合两个 ES6 +功能 - 箭头函数和属性初始值设置 - 选择加入到组件实例的绑定变得轻而易举：

    class PostInfo extends React.Component {
      handleOptionsButtonClick = (e) => {
        this.setState({ showOptionsModal: true });
      };
    }

1
2
3
4
5

> ES6 箭头函数的主体具有与它们周围 this 的代码相同的词汇，这使得我们获得了期望的结果，因为 ES7 属性初始化器的作用域的方式。在罩下看，看看为什么这个工作。

## # 动态属性名称和模板字符串

> 其中的增强对象文本包括分配给派生属性名称的能力。我们原本可以做这样的事情来设定一个状态：

    var From = React.createClass({
      onChange: function(inputName, e) {
        var stateToSet = {};
        stateToSet[inputName + 'Value'] = e.targe.value;
        this.setState(stateToset);
      },
    });
    // 现在，我们有能力构建在运行时由JavaScript表达式决定其属性名称的对象。在这里，我们使用模板字符串来确定要设置的属性：
    class From extends React.Component {
      onChange(inpuntName, e) {
        this.setState({
          [`$(inputName)value`]: e.targe.value,
        });
      }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

## # 解构和传播属性

> 通常在组成组件时，我们可能希望将大部分父组件的道具传递给子组件，但并不是全部。将 ES6 + 解构与 JSX 传播属性相结合，无需仪式即可实现：

    class AutoloadingPostsGrid extends React.Component {
        render() {
            const {
                className,
                ...others // 包含所有特性this.props除了类名
            } = this.props;
            return (
                <div className={className}>
                    <PostsGrid {...others} />
                    <button onClick={this.handleLoadMoreClick}>Load more</button>
                </div>
            );
        }
    }
    // 我们可以将JSX传播属性与常规属性结合起来，利用简单的优先级规则来实现覆盖和默认值。该元素将获得className“覆盖”，即使存在一个className财产this.props：
    <div {...this.props} className="override">
        ...
    </div>
    // This element will regularly have the “base” unless there exists a property in to override it:
    <div className="base" {...this.props}>
      ...
    </div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

## # 模块

    // 引用
    // es5
    var React = require("react");
    var {
        Component,
        PropsTypes
    } = React; //引用react抽象组件

    var ReactNative = require("react-native");
    var {
        Image,
        Text,
    } = ReactNative; // 引用具体的React Native组件

    // es6 import写法更为标准
    import React, {
        Component,
        PropsTypes,
    } from 'react';
    import {
        imange，
        Text,
    } from 'react-native'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

## # 导出单个类

> 在 ES5 里，要导出一个类给别的模块用，一般通过 module.exports 来导出

    // es5
    var MyComponent = React.createClass({
      // 函数
    });
    // es6
    export default class MyComponent extends Component {
      // 函数
    }
    // 引用 class
    // es5
    var MyComponent = require('./MyComponent');
    // es6
    import MyComponent from './MyComponent';

1
2
3
4
5
6
7
8
9
10
11
12
13

## # 定义组件

> es5 在 ES5 里，通常通过 React.createClass 来定义一个组件类，像这样：

    var Photo = React.createClass({
      render: function() {
        return <Image source={this.props.source} />;
      },
    });

1
2
3
4
5

> 在 ES6 里，我们通过定义一个继承自 React.Component 的 class 来定义一个组件类，像这样：

    class Photo extends React.Component {
      render() {
        return <Image source={this.props.source} />;
      }
    }

1
2
3
4
5

## # 给组件定义方法

> 给组件定义方法不再用 名字: function()的写法，而是直接用名字()，在方法的最后也不能有逗号了。

    // es5
    var Photo = React.createClass({
        componentWillMount: function() {
            // 函数
        },
        render: function() {
            return (
                <Image source={this.props.source} />
            );
        },
    });

    // es6
    class Photo extends React.Component {
        componentWillMount() {
            // 函数
        },
        render() {
            return (
                <Image source={this.props.source} />
            );
        }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

## # 定义组件的属性类型和默认属性

> 在 ES5 里，属性类型和默认属性分别通过 propTypes 成员和 getDefaultProps 方法来实现

    // es5
    var video = React.createClass({
        getDefaultProps: function() {
            return {
                autoPlay: false,
                maxloops: 10,
            };
        },
        PropsTypes: {
            autoPlay: React.PropsTypes.bool.isRequired,
            maxloops: React.PropsTypes.number.isRequired,
            posterFrameSrc: React.PropsTypes.string.isRequired,
            videoSrc: React.PropsTypes.string.isRequired,
        },
        render: function() {
            return: function() {
                <View />
            };
        }
    })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

> 在 ES6 里，可以统一使用 static 成员来实现

    //es6
    class Video extends React.Component {
      static defaultProps = {
        autoPlay: flase,
        maxloops: 10,
      }; // 注意这里有分号
      static propsTypes = {
        autoPlay: React.PropsTypes.bool.isRequired,
        maxloops: React.PropsTypes.number.isRequired,
        posterFrameSrc: React.PropsTypes.string.isRequired,
        videoSrc: React.PropsTypes.string.isRequired,
      }; // 注意这里有分号
      render() {
        return <view />;
      }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

> 也有人这么写，虽然不推荐，但读到代码的时候你应当能明白它的意思：

    //es6
    class Videlo extends React.Component {
      render() {
        return <Video />;
      }
    }
    Video.defauleftProps = {
      autoPlay: false,
      maxloops: 10,
    };
    Video.propsTypes = {
      autoPlay: React.PropsTypes.bool.isRequired,
      maxloops: React.PropsTypes.number.isRequired,
      posterFrameSrc: React.PropsTypes.string.isRequired,
      videoSrc: React.PropsTypes.string.isRequired,
    };
    // 注意: 对React开发者而言，static成员在IE10及之前版本不能被继承，而在IE11和其它浏览器上可以，这有时候会带来一些问题。React Native开发者可以不用担心这个问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

## # 初始化 STATE

    // es5
    var video = React.createClass({
      getInitalState: function() {
        return {
          loopsRemaining: this.props.maxloops,
        };
      },
    });
    // ES6下，有两种写法：
    class Video extends React.Component {
      state = {
        loopsRemaining: this.props.maxloops,
      };
    }
    // 不过我们推荐更易理解的在构造函数中初始化（这样你还可以根据需要做一些计算）：
    // es6
    class Video extends React.Component {
      construtor(props) {
        super(props);
        this.state = {
          loopsRemaining: this.props.maxloops,
        };
      }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

## # 把方法作为回调提供

> 很多习惯于 ES6 的用户反而不理解在 ES5 下可以这么做：

    var PostInfo = React.createClass({
      handleOptionsButtonClick: function(e) {
        // Here,'this' refers to the component instance
        this.setState({ showOptionModel: true });
      },
      render: function() {
        return (
          <TouchableHighlight onPress={this.handleOptionsButtonClick}>
            <Text>{this.props.label}</Text>
          </TouchableHighlight>
        );
      },
    });

1
2
3
4
5
6
7
8
9
10
11
12
13

> 在 ES5 下，React.createClass 会把所有的方法都 bind 一遍，这样可以提交到任意的地方作为回调函数，而 this 不会变化。但官方现在逐步认为这反而是不标准、不易理解的。

在 ES6 下，你需要通过 bind 来绑定 this 引用，或者使用箭头函数（它会绑定当前 scope 的 this 引用）来调用

    // es6
    class PostInfo extends React.Component
    {
        handleOptionsButtonClick(e){
            this.setState({showOptionsModal: true});
        }
        render() {
            return (
                <TouchableHighlight
                    onpress={this.handleOptionsButtonClick.bind(this)}
                    onpress={e=>this.handleOptionsButtonClick(e)}
                    >
                    <Text>{this.props.label}</Text>
                </TouchableHighlight>
            )
        },
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

## # 箭头函数

> 箭头函数实际上是在这里定义了一个临时的函数，箭头函数的箭头=>之前是一个空括号、单个的参数名、或用括号括起的多个参数名，而箭头之后可以是一个表达式（作为函数的返回值），或者是用花括号括起的函数体（需要自行通过 return 来返回值，否则返回的是 undefined）。

    // 箭头函数实例
    ()=>1
    v=>v+1
    (a,b)=>a+b
    ()=>{
        alert('foo');
    }
    e=>{
        if (e == 0){
            return 0;
        }
        return 1000/e;
    }

1
2
3
4
5
6
7
8
9
10
11
12
13

> 需要注意的是，不论是 bind 还是箭头函数，每次被执行都返回的是一个新的函数引用，因此如果你还需要函数的引用去做一些别的事情（譬如卸载监听器），那么你必须自己保存这个引用

    // 错误的做法
    class PaussMenu extends React.Component {
      componentWillMount() {
        AppStateIOS.addEventListener('change', this.onAppPaused.bind(this));
      }
      componentDidUnmount() {
        AppStateIOS.removeEventListener('change', this.onAppPaused.buid(this));
      }
      onAppPaused(event) {}
    }

    // 正确的做法
    class PaussMenu extends React.Component {
      construtor(props) {
        super(props);
        this._OnAppPaused = this.onAppPaused.bind(this);
      }
      componentWillMount() {
        AppStateIOS.addEventListener('change', this._OnAppPaused);
      }
      componentDidUnmount() {
        AppStateIOS.removeEventListener('change', this_onAppPaused);
      }
      onAppPaused(event) {}
    }
    // 从这个帖子[这个帖子](http://www.tuicool.com/articles/Rj6RFnm)中我们还学习到一种新的做法：
    // 正确的做法
    class PaussMenu extends React.createClass {
      componentWillMount() {
        AppStateIOS.addEventListener('change', this.onAppPaused);
      }
      componentDidUnmount() {
        AppStateIOS.removeEventListener('change', this.onAppPaused);
      }
      onAppPaused = (event) => {
        // 把方法直接作为一个arrow function的属性来定义，初始化的时候就绑定好了this的指针
      };
    }

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

## # Mixins

> 在 ES5 下，我们经常使用 minxin 来为我们的类添加一些新的方法，譬如 PureRenderMinxin

    var PurRenderMixin = require('react-addins-pure-render-minxin');
    React.createClass({
      mixin: [PurRenderMixin],

      render: function() {
        return <div className={this.props.className}>foo</div>;
      },
    });

1
2
3
4
5
6
7
8

> 然而现在官方已经不再打算在 ES6 里继续推行 Mixin，他们说：Mixins Are Dead. Long Live Composition。
> 尽管如果要继续使用 mixin，还是有一些第三方的方案可以用，譬如这个方案
> 不过官方推荐，对于库编写者而言，应当尽快放弃 Mixin 的编写方式，文中提到 Sebastian Markbåge 的一段代码推荐了一种新的编码方式：

    //Enhance.js
    import { Component } from 'React'

    extends var Enhance = ComposedComponent => class extends Component {
        construtor() {
            this.state = {dtaa:null}
        }
        componentDidUnmount() {
            this.setState({ data: 'Hello' });
        }
        render() {
            renturn <ComposedComponent {...this.props} data={this.state.data} />
        }
    }

    // HigherOrderComponent.js
    import { Enhance } from "./Enhance";

    class MyComponent {
        render() {
            if (!this.data) return <div>Waiting...</div>;
            return <div>{this.data}</div>;
        }
    }

    export defaule Enhance(MyComponent); // Enhance component
    //用一个“增强函数”，来某个类增加一些方法，并且返回一个新类，这无疑能实现mixin所实现的大部分需求。

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

## # ES6+带来的其它好处

### # 解构&属性延展

> 结合使用 ES6+的解构和属性延展，我们给孩子传递一批属性更为方便了。这个例子把 className 以外的所有属性传递给 div 标签：

    class AutoloadingPostsGrid extends React.Component {
        render() {
            var {
                className,
                ...others,
            } = this.props;
            return (
                <div className={className}>
                    <PostsGrid {...others} />
                    <button onClick={this.handleOptionsButtonClick}>Load more</button>
                </div>
            );
        }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14

> 下面这种写法，则是传递所有属性的同时，用覆盖新的 className 值：

    <div {...this.props} className="override">
      ...
    </div>

1
2
3

> 这个例子则相反，如果属性中没有包含 className，则提供默认的值，而如果属性中已经包含了，则使用属性中的值

    <div className="base" {...this.props}>
      ...
    </div>

1
2
3