🔗

bind(this)って何なん?

2022/12/01に公開

はじめに

Reactを勉強していたら,
関数の中に入れ子になってるthisundefinedになるので,bindしないといけません,と言われて,

と思ったことがあるかもしれません.

この記事は,そんな方のために,

と感じられるように説明していけたらと思います.

thisの意味は使う場所によって変わる

まず,thisをややこしくしている原因は,「thisの意味が一意ではなく,使う場所によって変化する」ということだと思います.
そして,この性質がbindの必要性とも関係してくるのです.

具体例を用いて考えましょう.以下のようなReactのクラスコンポーネントがあるとします.

MyComponent.jsx
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.handleClickthis,2つ目は,そのhandleClickメソッド呼び出し時に参照される,handleClickメソッド内のthis.setStatethisです.
1つ目に関しては,①の状況と同じです.つまり,renderメソッド内のthisなので,renderメソッドの親クラス(WhatIsBindクラス)のインスタンスを意味します.

では,2つ目はどうでしょう?
handleClickメソッド内のthis.setStatethisなので,これこそ①と同じじゃないの?と思うかもしれません.
ただ,thisは使われている場所によって意味が変わるので,名前が一致しているかではなく,状況が一致しているかという基準で判断する必要があります.
この基準で考える場合,2つ目のthisは,renderメソッド内で呼び出される,handleClickメソッド内のthis.setStatethisなので,renderメソッドがある分,thisが使われている階層が下になるので,状況が異なります.

では,この場合のthisはどんな意味を表しているのでしょうか?
➀の時と同じ感じで考えてみると,親クラスは何でしょう?そのインスタンスメソッドとは何を表しているのでしょう?

・・・

よくわからないですよね.

実は,「よくわからない」が正解なんです.
この場合,コンピュータ側も「よくわからない」となってしまうのです.
ただ,コンピュータは「よくわからない」ではなく,「setStateなんてありません.未定義(undefined)です」と反応します.

MyComponent.jsx
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が表示されることから明らかです.

console
undefined

このように,thisは,使う場所に応じて意味が変わるという性質上,意図しない参照エラーを起こす可能性をはらんでいます.

.bind(this)の意味

ここまで来ると,じゃあ,「thisの意味を固定できる方法もあった方がいいんじゃない?」と感じた方もいるかと思います.

このニーズにこたえるのが,bindメソッドになります.
具体的には以下のように使います.

MyComponent.jsx
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の使用を省略することが出来ます.
具体的には以下のように書くことが出来ます.

MyComponent.jsx
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は使う場所によって意味が変わるという性質があることを理解したうえで上手く使っていくことが大切だと思います.

では,

参考

https://medium.com/byte-sized-react/what-is-this-in-react-25c62c31480
https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/react/bind-this-to-a-class-method

GitHubで編集を提案

Discussion