ReactComponentでthisがundefinedになる問題の解決策


2015-07-06-prevent-undefiend-this-in-react

最近、ShouldBeeのUIをReact.jsで作り始めています。

Babelを使ってES6のclass構文でReactのComponentを作っているのですが、どうもコールバックのメソッドでthisundefinedになってしまいます。

例えば、下のJSはボタンのコンポーネントで、クリックしたときに、onClickメソッドが呼ばれます。onClickメソッドの中で、thisを参照していますが、undefinedになります。

import React from "react";

export default class Button extends React.Component {
  onClick() {
    console.log(this); // undefined
  }

  render() {
    return (
      <button onClick={this.onClick}>Button</button>
    );
  }
}

ここでは、この問題を解決する方法を紹介したいと思います。解決方法は4つあります。

解決策1: XMLの中でbind

1つ目はXMLの中でthisをbindする方法です。

  render() {
    return (
      <button onClick={this.onClick.bind(this)}>Button</button>
    );
  }

解決策2: Autobindingを使う

React v0.13.0 Beta 1 | Reactで紹介されている方法です。コンストラクタでバインドするというものです。

export default class Button extends React.Component {
  constructor() {
    super();
    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    console.log(this);
  }

  render() {
    return (
      <button onClick={this.onClick}>Button</button>
    );
  }
}

解決策3: @autobindデコレーターを使う

ES7で提案されている機能のデコレーターを使う方法です。andreypopp/autobind-decoratorというライブラリが公開されていて、これをnpmでインストールしておくとメソッドに対して、@autobindデコレーターを使えるようになります。

export default class Button extends React.Component {
  @autobind
  onClick() {
    console.log(this);
  }

  render() {
    return (
      <button onClick={this.onClick}>Button</button>
    );
  }
}

解決策4: Function Bind Syntaxを使う

Function Bind SyntaxもまたES7で提案されている機能のひとつで、メソッドの頭に::をつけるとthisがbindできるというものです。::foofoo.bind(this)のシュガーシンタクスになります。React Componentの場合はXMLの中でonClick={::this.onClick}のように使うことができます。

export default class Button extends React.Component {
  onClick() {
    console.log(this);
  }

  render() {
    return (
      <button onClick={::this.onClick}>Button</button>
    );
  }
}

以上が解決策の紹介になります。なお、解決策3,4については、記事執筆時点でES7の提案中の構文になるので、変更または却下されている可能性があります。