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のスコープは奥が深いというかクセがある。。