okashi
@okashi (oyatsu daisuki)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Reactによく出てくるthisなどいくつか理解できない

Q&A

Closed

React初心者です(JSも初級者です)。
Reactによく出てくる thisが何を指しているか理解できないなど、いくつかわからないことがあり、質問させていただきます。

1.[わからない1]:super(props)のpropsに何が入っているか
2.[わからない2]:this.handleChange = this.handleChange.bind(this);の bind(this)に何が入っているかわからない
3.[わからない3]:handleSubmitメソッドの中の、event.preventDefault();が何のためにあるかわからない
4.[わからない4]:thisの中身を確認する方法 console.log(this);

どうぞよろしくお願いします。

下のコードは、
- ReactのFormを解説してあるページ
- 動くサンプルcodepen
にあった
this.handleChange = this.handleChange.bind(this); です。

FlavorForm.js
class FlavorForm extends React.Component {

//FlavorFormというクラスのコンストラクタ(最初に必ず読み込まれる部分)
//[わからない1]:propsには引数、ここだとselectのvalueが入る? でもthis.stateには入っているのがわかるけど、 super(props)のpropsにはどうやって入るのかわからない

  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};

//[わからない2]:このクラスの handleChangeメソッドは、setStateでイベントが発生した時に発動するのはわかるが、3つ目のthisに何が入っているかがよくわからない、変更したvalueの内容?かとは思うのですが、、
//(このクラスの).handleChange = (このクラスの).handleChange.bind(変更したvalueの内容?);

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

//formのselectのvalueが変化した時のメソッド
//引数のeventに、valueの値が入る
  handleChange(event) {
    this.setState({value: event.target.value});
  }

//formが送信された時にアラートを出すメソッド
//引数のeventに、this.stateのvalueが入る
//[わからない3]: なぜpreventDefaultがしてあるのか?がわからない
  handleSubmit(event) {
    alert('Your favorite flavor is: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Pick your favorite flavor:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">Grapefruit</option>
            <option value="lime">Lime</option>
            <option value="coconut">Coconut</option>
            <option value="mango">Mango</option>
          </select>
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

//FlavarFormをrootにレンダー
ReactDOM.render(
  <FlavorForm />,
  document.getElementById('root')
);
1

2Answer

@uttk

この場合は、書いているClass(今回の場合は、FlavorFormクラス)を指しています。

this が指すのはクラスではなく FlavorForm のインスタンスですね。

(質問者以外は回答にコメントをつけられないようなので回答にてレスしてます、ご容赦ください)


せっかくなので this.handleChange = this.handleChange.bind(this); が何を意味するかについて補足回答します。

handleChangeFlavorForm クラスのメソッドとして定義されていますが、 JavaScript ではメソッドの中の this がいつでもインスタンスを指すとは限りません。

class FlavorForm extends React.Component {
  ...
  handleChange(event) {
    this.setState({value: event.target.value});
//  ^^^^ この this の値が FlavorForm のインスタンスとは限らない。
  }
}

this がインスタンス以外を指すのは以下の状況です。

const form = new FlavorForm();

// メソッドの関数オブジェクトを取り出して関数呼び出しの形にすると、
// this は global か undefined になる(呼ぶ場所による)。
const onChange = form.handleChange;
onChange();

// .call() か .apply() で明示的に this の値を与えることもある。
form.handleChange.call(myThis);

そして React ではメソッドの関数オブジェクトを取り出す場合がままあります。

<select value={this.state.value} onChange={this.handleChange}>
//                                        ^^^^^^^^^^^^^^^^^^^
                                        

this の値がどうなるか分からないのは困りますから、 .bind() で関数内の this をあらかじめ固定する手法がよく使われます。

  constructor(props) {
    ...
    this.handleChange = this.handleChange.bind(this);
//                      ^^^^^^^^^^^^^^^^^ (1)
//                                       ^^^^^^^^^^^ (2)
//  ^^^^^^^^^^^^^^^^^ (3)
  }

// 1. handleChange プロパティから関数オブジェクトを取り出し、
// 2. その中の this を今ここの this (= FlavorForm のインスタンス)
//    で固定した新しい関数オブジェクトを作り、
// 3. 元のプロパティに代入してメソッドを上書きする。

こうすれば handleChange の中の this はいつでも FlavorForm のインスタンスになります。

2Like

Comments

  1. @okashi

    Questioner

    @uasi さん、わかりやすく丁寧なコメントをありがとうございます。やはりまだ理解しきれていないためさらに質問させて頂けないでしょうか。

    1.
    > // .call() か .apply() で明示的に this の値を与えることもある。
    > form.handleChange.call(myThis);

    ここの myThisはFlavorFormということでいいでしょうか?

    2.
    // 1. handleChange プロパティから関数オブジェクトを取り出し、
    > this.setState({value: event.target.value}); のことでしょうか

    // 2. その中の this を今ここの this (= FlavorForm のインスタンス)
    // で固定した新しい関数オブジェクトを作り、
    > これはbind(this)のthis (= FlavorForm のインスタンス)ということですよね
    FlavorFormクラスにある、handleChangeメソッドのインスタンスが、bind(this)のthisに入っているということでしょうか

    3.
    // 3. 元のプロパティに代入してメソッドを上書きする。
    FlavorFormのhandleChangeメソッドのプロパティに代入ということでしょうか

    thisがいまだに理解しきれておらず、、どうぞよろしくお願いします!


@okashi

// .call() か .apply() で明示的に this の値を与えることもある。
form.handleChange.call(myThis);

ここの myThisはFlavorFormということでいいでしょうか?

myThisFlavorForm のインスタンスである必要はなく、任意の値を渡すことができます。 ただ this を任意の値にセットしたい局面は実用上ほとんどないので、大抵は form.handleChange.call(form) のようにオブジェクト自身を渡すことになります。そもそも call を呼ぶこともめったにないですから、これは this を差し替え可能な例ということで軽く流してください。

this.setState({value: event.target.value}); のことでしょうか

それを本文とする関数オブジェクトです。JavaScript の関数は Function クラスのオブジェクトであり、値として扱えます。

const f = (function() { console.log("hello") });
f(); // => hello

コンストラクタの処理に入った時点で、 this.handleChange には以下のコードを実行したかのように関数オブジェクトがすでに入っていると考えてください。(厳密にいうとプロトタイプが絡んでくるのですが、今は気にしなくていいです。)

this.handleChange = (function(event) {
  this.setState({value: event.target.value});
});

FlavorFormクラスにある、handleChangeメソッドのインスタンスが、bind(this)のthisに入っているということでしょうか

コンストラクタの中ですから、 bind(this)this は組み立て中の FlavorForm インスタンスです。

  constructor(props) {
    ...
    this.handleChange = this.handleChange.bind(this);
 // ^^^^                ^^^^                   ^^^^
 // これらの this は全部同じ。組み立て中の FlavorForm のインスタンス。

// 3. 元のプロパティに代入してメソッドを上書きする。

FlavorFormのhandleChangeメソッドのプロパティに代入ということでしょうか

JavaScript では「クラスが hoge インスタンスメソッドを持つ」とは「インスタンスの hoge プロパティに関数オブジェクトが入っている」のと(ほぼ)同義である、ということを踏まえて言い直すと、「FlavorForm インスタンスの handleChange プロパティに代入」です。


元々のコードは this がいくつも出たりクラスとメソッドの関係に触れたりとややこしいので、プレーンなオブジェクトの形に書き直してみました。このほうが何が起きているか分かりやすいかと思います。

const form = {
  // ↓特に意味なし。ログに名前が出れば this が何か分かりやすいので入れた。
  name: 'form 1',
  // ↓プロパティに関数オブジェクトを入れるといわゆるメソッドになる。
  handleChange: function(e) { console.log(`Event: ${e}, this:`, this); }
};

form.handleChange('HogeEvent');
// => Event: HogeEvent, this: { name: 'form 1', handleChange: [Function: handleChange] }
// メソッド形式で呼び出すと this == form

const f = form.handleChange;
f('HogeEvent');
// => Event: HogeEvent, this: <ref *1> Object [global] { ... }
// 関数形式で呼び出すと this が変わる!

form.handleChange = form.handleChange.bind(form);
form.handleChange('HogeEvent');
// => Event: HogeEvent, this: { name: 'form 1', handleChange: [Function: bound handleChange] }

const g = form.handleChange;
g('HogeEvent');
// => Event: HogeEvent, this: { name: 'form 1', handleChange: [Function: bound handleChange] }
// bind しておけば変わらない!
2Like

Comments

  1. @okashi

    Questioner

    ありがとうございます!!

    1.大抵は form.handleChange.call(form) のようにオブジェクト自身を渡すことができるが、
    任意の値にセットすることもできる

    2.this.handleChange = this.handleChange.bind(this);
    のthisには全てFunction クラスのオブジェクトが入っている。

    3.「クラスが hoge インスタンスメソッドを持つ」とは「インスタンスの hoge プロパティに関数オブジェクトが入っている」

    理解できたような気がします。thisについては多分まだ把握しきれていないのですが、
    このプログラムの意味がわかりました。
    もう一度読み直してちゃんと理解しようと思います。
    貴重なご指導ありがとうございました。

    プログラミングの勘が全くなく、とにかく理解しようとしているのですが、
    やはり具体的にものを作っていったほうが良さそうな気がしてきました。

Your answer might help someone💌