dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

ES6とwebpackでReact.jsのチュートリアルをやってみた

タイトルの通り、こちらの公式チュートリアルをES6でやってみました。

https://facebook.github.io/react/docs/tutorial-ja-JP.html

また、チュートリアルでは React や Babel などを CDN で読み込んでいるためビルドは不要になってますが、 実際に開発するときは browserify なり webpack なりを使用したビルド環境が必要になると思うので
今回は webpack を使った開発環境の構築についても試してみました。

ソースコードはこちら:

同じことを既にやっている方がいて、環境構築手順とか webpack.config.js の書き方とか
ほとんどこちらの記事の通りにさせていただきました。
大変参考になりました。

ReactのチュートリアルをES6で書いてwebpackとESLintも使ってみる - Qiita

簡易サーバーは Python で書いてます。 また、webpack.config.js については整理してまた後日メモしようと思います。

React を ES6 で書くときの注意点とか

個人的にハマったというか勉強になったのは以下の2点ぐらいでしょうか。

1. getInitialState() は ES6 の構文では使えない

参考:http://facebook.github.io/react/docs/reusable-components.html#es6-classes

The API is similar to React.createClass with the exception of getInitialState. Instead of providing a separate getInitialState method, you set up your own state property in the constructor.

なので、かわりにコンストラクタ内で行います。

before (ES5)
var CommentBox = React.createClass({
  getInitialState: function() {
    return {data: []};
  },
  // ...
after (ES6)
export default class CommentBox extends React.Component {
  constructor(props) {
    super(props);
    this.state = {data: []};
  }
  // ...

ちなみに、今回のチュートリアルで登場しなかった propTypesdefaultProps についてはクラスの外で定義するようです。

before (ES5)
var Counter = React.createClass({
  propTypes: {
    initialCount: React.PropTypes.number
  },

  getDefaultProps: function() {
    return {
      initialCount: 0
    };
  },
  // ...
after (ES6)
export default class Counter extends React.Component {
  // ...
}
Counter.propTypes = { initialCount: React.PropTypes.number };
Counter.defaultProps = { initialCount: 0 };


2. ES6 で書くときはインスタンスメソッドをbindする必要がある

これも公式ドキュメントに書いてあります。

参考:http://facebook.github.io/react/docs/reusable-components.html#no-autobinding

Methods follow the same semantics as regular ES6 classes, meaning that they don't automatically bind this to the instance. You'll have to explicitly use .bind(this) or arrow functions =>.

そして、この問題を回避するやり方はいくつかあるみたいです。
参考:Reactをes6で使う場合のbindの問題 - Qiita

コンストラクタで bind する以下のような方法もありますが

export class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: props.initialCount};
    this.tick = this.tick.bind(this);  // ここで bind
  }
  tick() {
    this.setState({count: this.state.count + 1});
  }
  // ...

私は公式ドキュメントのサンプルコードにならって以下の書き方で統一することにしました。

export class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: props.initialCount};
  }
  tick() {
    this.setState({count: this.state.count + 1});
  }
  render() {
    return (
      <div onClick={this.tick.bind(this)}>  // ここで bind
        Clicks: {this.state.count}
      </div>
    );
  }
}

(2016/04/27追記)

Airbnb React/JSX Style Guide によるとコンストラクタで bind する方を推奨しているようです。
https://github.com/airbnb/javascript/tree/master/react#methods

Bind event handlers for the render method in the constructor. eslint: react/jsx-no-bind

Why? A bind call in the render path creates a brand new function on every single render.

ということから、パフォーマンスを考慮すると前者の方が良い...?