タオルケット体操

サツバツいんたーねっと

ES6のArrow FunctionとReact.createClassのthisの組み合わせで気をつけたいこと

ハマったわけですが、原因は僕がちゃんとES6の仕様を理解してなかったのが悪いわけです。基本をおろそかにすると余計な時間を食うという良い例です。
しかし動的にブレまくるくそったれのthisが、急にまともな挙動をしてくれたことでハマってしまうあたり僕も随分ジャバScriptに毒されてきたみたいです。

Arrow Functionのthisの挙動

最近のReactではClass構文でのReact.Componentの継承をサポートしてますが、ES6にはmixinにあたる機能がないために brigand/react-mixin · GitHub のようなヘルパーを使うか、今まで通り createClass を使ってmixinするかのどちらかしかありません。
メタFluxフレームワークのArdaなんかを使ってたりすると、必然的にmixin前提になってくるわけですね。

react-mixinは本能的な不安を感じるわけで、仕方なくcreateClassするわけです。

どうせならプロパティメソッドはカッコよくかきたい。function() とか書きたくないわけです。

ここで注意しないといけない点が二つあります。

  1. Arrow Functionのthisは常に静的に決定される

  2. ES6 Modulesの仕様において、Top Levelのthisはundefined

はい。
つまり、Arrow Function内で、Top Levelのthisを参照するようなコードはbabelがundefinedに変換するっつーことです。ワオ! ファンタスティック! ビルドエラーにしてくれ!

簡単な例

なんのこっちゃという人のために簡単な例です


let test = {
  foo: () => {
    this.a = 1;
  },
  bar: function() {
    this.a = 1;
    let boo = () => {
      this.c = 3;
      this.d = function(aa) {
        const hoge = () => {
          console.log(this.c);
        };
        hoge();
      };
    };
  }
};

これが

"use strict";

var test = {
  foo: function foo() {
    undefined.a = 1;
  },
  bar: function bar() {
    var _this = this;

    this.a = 1;
    var boo = function boo() {
      _this.c = 3;
      _this.d = function (aa) {
        var _this2 = this;

        var hoge = function hoge() {
          console.log(_this2.c);
        };
        hoge();
      };
    };
  }
};

こうなります。

余計なコードがあるんでアレかもしんないですが、 foo: の中にあるthisがundefinedになっているのがわかるとおもいます。
これをReactのrender内でやらかすと、黙ったままアプリが死にます。デバッグできません。(僕のdev tool力が低い可能性も)

こうならないためにも、bar: のようにthisを静的に決定できない環境ではArrow Functionを避けて、今まで通りFunction式を使うべきでしょう。

よりよい解決策

そもそもの話になりますけど、ES6ではメソッドプロパティを書くときにfunctionを省略できます。

なので

let test2 = {
  foo() {
    this.a = 1;
  },

  bar() {
    this.b = 2;
  }
};

と書けばいいわけですね。そういえばそうだった。

もちろん、これはオブジェクトのプロパティとして関数を登録するときの話なわけで、Top Levelのただの関数や、コールバックに渡す関数式に関してはFunction式を使うべきでしょうね。*1

まとめ

  • Arrow Functionは「なんとなくthisをいい感じにしてくれるだけのアレ」ではない

  • functionとの使い分けを考える

  • オブジェクトのプロパティに関数を登録するときは、functionを省略できる。

  • ES7でよくわかんない新機能を追加しまくるよりも、もっとここら辺の落とし穴を人間に優しい感じに修正してくれると嬉しいよね(super()の前にコード書いたときはちゃんとエラー吐いてくれるのに……)

  • Reactのデバッグ大変すぎワロタ

挙動とかの詳しいところはGitHubにissueがいっぱいあるんでそれをみましょう(やっぱハマるよねみんな)。

github.com


速習ECMAScript6: 次世代の標準JavaScriptを今すぐマスター!

速習ECMAScript6: 次世代の標準JavaScriptを今すぐマスター!

*1:余談ですけど、jQueryであるような、コールバック内でthisを参照するような書き方って大嫌いなんですけどなんで普通に引数にしないんでしょうね