bind(this)って何なん?
はじめに
Reactを勉強していたら,
関数の中に入れ子になってるthis
はundefined
になるので,bind
しないといけません,と言われて,
と思ったことがあるかもしれません.
この記事は,そんな方のために,
と感じられるように説明していけたらと思います.
this
の意味は使う場所によって変わる
まず,this
をややこしくしている原因は,「this
の意味が一意ではなく,使う場所によって変化する」ということだと思います.
そして,この性質がbind
の必要性とも関係してくるのです.
具体例を用いて考えましょう.以下のようなReactのクラスコンポーネントがあるとします.
class WhatIsBind extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "No idea"
};
}
// ➀定義時
handleClick() {
this.setState({
text: "Understood!"
});
}
render() {
return (
<div>
{/* ➁呼び出し時 */}
<button onClick={this.handleClick}>Finished Reading</button>
<h1>{this.state.text}</h1>
</div>
);
}
};
ここで,handleClick
メソッドはクラスコンポ―ネント内で定義され,render
メソッドのreturn
文の中のbutton
タグでclick時に呼び出されるように設定されています.
つまり,handleClick
メソッドの中のthis.setState
は➀定義時と,clickによる➁呼び出し時に参照されることになります.
それぞれの場合で,this
が何を意味するのかを考えていきましょう.
this
参照時の状況
➀定義時
この場合,this
は,親クラスのインスタンスを表します.
つまり,this.setState
は,React.Component
クラスを継承した,WhatIsBind
クラスのインスタンスである,setState
メソッドを示しています.
この場合,setState
メソッドは,React.Component
クラスのインスタンスメソッドとして,実際に定義されているので,何の問題もなくthis
を参照することが出来ています.
➁呼び出し時
では,呼び出し時はどうでしょう?
ここでは,2種類のthis
が参照されていることに注意が必要です.
1つ目は,this.handleClick
のthis
,2つ目は,そのhandleClick
メソッド呼び出し時に参照される,handleClick
メソッド内のthis.setState
のthis
です.
1つ目に関しては,①の状況と同じです.つまり,render
メソッド内のthis
なので,render
メソッドの親クラス(WhatIsBind
クラス)のインスタンスを意味します.
では,2つ目はどうでしょう?
handleClick
メソッド内のthis.setState
のthis
なので,これこそ①と同じじゃないの?と思うかもしれません.
ただ,this
は使われている場所によって意味が変わるので,名前が一致しているかではなく,状況が一致しているかという基準で判断する必要があります.
この基準で考える場合,2つ目のthis
は,render
メソッド内で呼び出される,handleClick
メソッド内のthis.setState
のthis
なので,render
メソッドがある分,this
が使われている階層が下になるので,状況が異なります.
では,この場合のthis
はどんな意味を表しているのでしょうか?
➀の時と同じ感じで考えてみると,親クラスは何でしょう?そのインスタンスメソッドとは何を表しているのでしょう?
・・・
よくわからないですよね.
実は,「よくわからない」が正解なんです.
この場合,コンピュータ側も「よくわからない」となってしまうのです.
ただ,コンピュータは「よくわからない」ではなく,「setState
なんてありません.未定義(undefined
)です」と反応します.
class WhatIsBind extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "No idea"
};
}
// ➀定義時
handleClick() {
- this.setState({
- text: "Understood!"
- });
+ console.log(this);
}
render() {
return (
<div>
{/* ➁呼び出し時 */}
<button onClick={this.handleClick}>Finished Reading</button>
<h1>{this.state.text}</h1>
</div>
);
}
};
これは,handleClick
メソッドを以上のように書き換えた後に,ボタンをクリックすると,コンソールにundefined
が表示されることから明らかです.
undefined
このように,this
は,使う場所に応じて意味が変わるという性質上,意図しない参照エラーを起こす可能性をはらんでいます.
.bind(this)の意味
ここまで来ると,じゃあ,「this
の意味を固定できる方法もあった方がいいんじゃない?」と感じた方もいるかと思います.
このニーズにこたえるのが,bind
メソッドになります.
具体的には以下のように使います.
class WhatIsBind extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "No idea"
};
+ this.handleClick = this.handleClick.bind(this)
}
// ➀定義時
handleClick() {
this.setState({
text: "Understood!"
});
}
render() {
return (
<div>
{/* ➁呼び出し時 */}
<button onClick={this.handleClick}>Finished Reading</button>
<h1>{this.state.text}</h1>
</div>
);
}
};
このように使うことで,WhatIsBind
クラスがインスタンス化された時点で,constructor
メソッドが実行され,handleClick
メソッド内で使うthis
は,「定義時のthis
の意味で固定」されます.
これにより,➁呼び出し時でも,意図したとおり,WhatIsBind
クラスのインスタンスである,setState
メソッドを参照することが出来ます.
bind
によるthis
の意味の固定
おまけ(アロー関数によるbind)
ちなみに,アロー関数を使うと,先ほどのbind
の使用を省略することが出来ます.
具体的には以下のように書くことが出来ます.
class WhatIsBind extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "No idea"
};
- this.handleClick = this.handleClick.bind(this)
}
// ➀定義時
+ handleClick = () => {
this.setState({
text: "Understood!"
});
}
render() {
return (
<div>
{/* ➁呼び出し時 */}
<button onClick={this.handleClick}>Finished Reading</button>
<h1>{this.state.text}</h1>
</div>
);
}
};
これは,アロー関数は,定義時のthis
が既に関数にbind
されているからです.
おわりに
ここまで見てきたように,this
は使う場所によって意味が変わるという性質があることを理解したうえで上手く使っていくことが大切だと思います.
では,
参考
Discussion