これが、Reactのクラスコンポーネントでイベントハンドラーをバインドする必要がある理由です。

Reactで作業しているときに、制御されたコンポーネントとイベントハンドラーに出くわしたはずです。.bind()カスタムコンポーネントのコンストラクターを使用して、これらのメソッドをコンポーネントインスタンスにバインドする必要があります。

class Foo extends React.Component{ constructor( props ){ super( props ); this.handleClick = this.handleClick.bind(this); } handleClick(event){ // your event handling logic } render(){ return (  Click Me  ); } } ReactDOM.render( , document.getElementById("app") );

この記事では、なぜこれを行う必要があるのか​​を説明します。

.bind()それが何をするのかまだわからない場合は、ここを読むことをお勧めします。

JavaScriptを非難し、反応しない

まあ、非難することは少し厳しいように聞こえます。これは、Reactの動作方法や、JSXのために、私たちが行う必要のあることではありません。これはthis、JavaScriptでのバインディングの動作方法によるものです。

イベントハンドラメソッドをそのコンポーネントインスタンスにバインドしないとどうなるか見てみましょう。

class Foo extends React.Component{ constructor( props ){ super( props ); } handleClick(event){ console.log(this); // 'this' is undefined } render(){ return (  Click Me  ); } } ReactDOM.render( , document.getElementById("app") );

このコードを実行する場合は、「Click Me」ボタンをクリックして、コンソールを確認してください。次のように表示されますundefinedの値としてコンソールに印刷されたthisイベントハンドラメソッドの内部から。handleClick()この方法はしているようだ失われたコンテキスト(コンポーネントインスタンス)またはthis値。

JavaScriptでの「this」バインディングのしくみ

前述したように、これthisはJavaScriptでのバインディングの動作方法が原因で発生します。この投稿ではあまり詳しく説明しませんがthis、JavaScriptでバインディングがどのように機能するかを理解するための優れたリソースがあります。

ただし、ここでの説明に関連してthis、関数内の値は、その関数がどのように呼び出されるかによって異なります。

デフォルトのバインディング

function display(){ console.log(this); // 'this' will point to the global object } display(); 

これは単純な関数呼び出しです。この場合thisdisplay()メソッド内の値は、非厳密モードのウィンドウ(またはグローバル)オブジェクトです。strictモードでは、this値はundefinedです。

暗黙のバインディング

var obj = { name: 'Saurabh', display: function(){ console.log(this.name); // 'this' points to obj } }; obj.display(); // Saurabh 

この方法で関数を呼び出すと(コンテキストオブジェクトが前に付きます)、this内部の値display()はに設定されobjます。

ただし、この関数参照を他の変数に割り当て、この新しい関数参照を使用して関数を呼び出すと、thisinsideの値が異なりますdisplay()

var name = "uh oh! global"; var outerDisplay = obj.display; outerDisplay(); // uh oh! global

上記の例では、を呼び出すときにouterDisplay()、コンテキストオブジェクトを指定していません。これは、所有者オブジェクトのない単純な関数呼び出しです。この場合、thisinsideの値はデフォルトのバインディングにdisplay()フォールバックしますグローバルオブジェクトを指しているundefinedか、呼び出されている関数が厳密モードを使用しているかどうかを示します。

これは、コールバックなどの関数を別のカスタム関数、サードパーティのライブラリ関数、またはのような組み込みのJavaScript関数に渡すときに特に当てはまりますsetTimeout

setTimeout以下に示すダミー定義を検討してから、それを呼び出します。

// A dummy implementation of setTimeout function setTimeout(callback, delay){ //wait for 'delay' milliseconds callback(); } setTimeout( obj.display, 1000 );

を呼び出すとsetTimeout、JavaScriptobj.displayがその引数に内部的に割り当てていることがわかりますcallback

callback = obj.display;

これまで見てきたように、この代入操作により、display()関数はコンテキストを失います。このコールバックが最終的に内部setTimeoutで呼び出されると、内部のthisdisplay()デフォルトのバインディングにフォールバックします

var name = "uh oh! global"; setTimeout( obj.display, 1000 ); // uh oh! global

明示的なハードバインディング

これを回避するにthisbind()メソッドを使用して値を関数に明示的にハードバインドします。

var name = "uh oh! global"; obj.display = obj.display.bind(obj); var outerDisplay = obj.display; outerDisplay(); // Saurabh

さて、を呼び出すとouterDisplay()、の値は内部をthis指します。objdisplay()

obj.displayコールバックとして渡しても、this内部の値display()は正しくを指しobjます。

JavaScriptのみを使用してシナリオを再作成する

この記事の冒頭で、これをReactコンポーネントで見ましたFoo。イベントハンドラーをthisでバインドしなかった場合、イベントハンドラー内の値はに設定されましたundefined

私が言及して説明したように、これはthisJavaScriptでのバインディングの動作方法によるものであり、Reactの動作方法とは関係ありません。それでは、React固有のコードを削除し、同様の純粋なJavaScriptの例を作成して、この動作をシミュレートしましょう。

class Foo { constructor(name){ this.name = name } display(){ console.log(this.name); } } var foo = new Foo('Saurabh'); foo.display(); // Saurabh // The assignment operation below simulates loss of context // similar to passing the handler as a callback in the actual // React Component var display = foo.display; display(); // TypeError: this is undefined

実際のイベントとハンドラーをシミュレートするのではなく、同義のコードを使用しています。React Componentの例で観察したように、this値はundefined、ハンドラーをコールバックとして渡した後にコンテキストが失われたときのものでした。これは、割り当て操作と同義です。これは、この非ReactJavaScriptスニペットでも観察されていることです。

"ちょっと待って!thisデフォルトのバインディングのルールに従ってこれを非厳密モードで実行しているので、値はグローバルオブジェクトを指すべきではありませんか?」あなたは尋ねるかもしれません。

いいえ。これが理由です。

クラス宣言クラス式の本体は、厳密モード、つまりコンストラクター、静的メソッド、およびプロトタイプメソッドで実行されます。ゲッター関数とセッター関数は厳密モードで実行されます。

あなたはここで完全な記事を読むことができます。

したがって、エラーを防ぐには、次のthisように値をバインドする必要があります。

class Foo { constructor(name){ this.name = name this.display = this.display.bind(this); } display(){ console.log(this.name); } } var foo = new Foo('Saurabh'); foo.display(); // Saurabh var display = foo.display; display(); // Saurabh

コンストラクターでこれを行う必要はなく、他の場所でもこれを行うことができます。このことを考慮:

class Foo { constructor(name){ this.name = name; } display(){ console.log(this.name); } } var foo = new Foo('Saurabh'); foo.display = foo.display.bind(foo); foo.display(); // Saurabh var display = foo.display; display(); // Saurabh

But the constructor is the most optimal and efficient place to code our event handler bind statements, considering that this is where all the initialization takes place.

Why don’t we need to bind ‘this’ for Arrow functions?

We have two more ways we can define event handlers inside a React component.

  • Public Class Fields Syntax(Experimental)
class Foo extends React.Component{ handleClick = () => { console.log(this); } render(){ return (  Click Me  ); } } ReactDOM.render( , document.getElementById("app") );
  • Arrow function in the callback
class Foo extends React.Component{ handleClick(event){ console.log(this); } render(){ return (  this.handleClick(e)}> Click Me  ); } } ReactDOM.render( , document.getElementById("app") );

Both of these use the arrow functions introduced in ES6. When using these alternatives, our event handler is already automatically bound to the component instance, and we do not need to bind it in the constructor.

The reason is that in the case of arrow functions, this is bound lexically. This means that it uses the context of the enclosing function — or global — scope as its this value.

In the case of the public class fields syntax example, the arrow function is enclosed inside the Foo class — or constructor function — so the context is the component instance, which is what we want.

In the case of the arrow function as callback example, the arrow function is enclosed inside the render() method, which is invoked by React in the context of the component instance. This is why the arrow function will also capture this same context, and the this value inside it will properly point to the component instance.

For more details regarding lexical this binding, check out this excellent resource.

To make a long story short

In Class Components in React, when we pass the event handler function reference as a callback like this

Click Me

the event handler method loses its implicitly bound context. When the event occurs and the handler is invoked, the this value falls back to default binding and is set to undefined , as class declarations and prototype methods run in strict mode.

When we bind the this of the event handler to the component instance in the constructor, we can pass it as a callback without worrying about it losing its context.

Arrow functions are exempt from this behavior because they use lexicalthisbinding which automatically binds them to the scope they are defined in.