React.Componentのメソッドを使う際にthisが正しく参照できないことがあった。その原因と対処法を記す。動かせるコードはこちら。
問題
Click Me!という文字列をクリックした時にThanksと文字列が切り替わるようなコンポーネントを実装したい。

ここで次のようなコードにすると、クリックしても文字が切り替わらない。
class Clickable extends React.Component {
constructor(props) {
super(props);
this.state = { msg: "Click Me!"};
};
render () {
return <h1 onClick={this.handleClick}>{ this.state.msg }</h1>;
};
handleClick () { this.setState({ msg: "Thanks!"}); };
}
ReactDOM.render(
<Clickable/>,
document.getElementById('mount')
);
原因はメソッドhandleClickを定義した時に、thisのスコープがメソッド内のものに置き換わってしまい、クラス内のスコープのthisを参照することが出来ないためだ。
正しく動作させるための対処法を以下に書いていく。
対処法①:コンストラクタで束縛する
最初の対処法はクラスのコンストラクタ内でクラスのスコープでのthisをメソッドに束縛させる方法だ。
class Clickable extends React.Component {
constructor(props) {
super(props);
this.state = { msg: "Click Me!"};
// 対処法① コンストラクタ内でhandleClickを束縛する
this.handleClick = this.handleClick.bind(this);
};
...
}
この方法はうまくいくが、束縛はコンストラクタ内で行う必要があるため、コンストラクタを省略することが出来なくなるという欠点がある。
対処法②:クロージャを使う
2番目の対処法はメソッド呼び出しの時にクロージャを使うことだ。renderが実行された時、クロージャによってクラスのthisが束縛されるので、メソッド内で正しく参照を行うことが出来るようになる。
render () {
return <h1 onClick={() => this.handleClick()}>{ this.state.msg }</h1>;
};
この書き方ではコンストラクタ内で束縛する必要がなくなるため、コンストラクタが不必要な場合は省略することが出来る。
対処方③:アロー関数を使う
最後の対処法はメソッドhandlwClickの定義にアロー関数表記を使うことだ。アロー関数は変数のリスコープを行わないので、元々のthisをメソッド内部でそのまま参照することが出来る。JavaScriptでは関数は第1級のオブジェクトであるため、アロー関数をクラスのメンバ変数として登録している形となっている。
handleClick = () => { this.setState({ msg: "Thanks!"}); };
この書き方でもコンストラクタ内で束縛を行う必要はないので、コードが短くなる。
まとめ
メソッド内でクラスのthisを参照する方法を3つ紹介した。
どうもJSのスコープは奥が深いというかクセがある。。