ホームページ  >  記事  >  ウェブフロントエンド  >  React コンポーネントでこれをバインドする理由の分析

React コンポーネントでこれをバインドする理由の分析

不言
不言オリジナル
2018-07-21 10:27:451263ブラウズ

この記事は、React コンポーネント メソッドでこれをバインドする必要がある理由について説明しています。必要な方はぜひ参考にしてください。

フロントエンド開発に React を使用しようとした場合は、次のコードを見たことがあるはずです: React进行前端开发,一定见过下面这样的代码:

//假想定义一个ToggleButton开关组件
class ToggleButton extends React.Component{
    constructor(props){
        super(props);
        this.state = {isToggleOn: true};
        this.handleClick = this.handleClick.bind(this); 
        this.handleChange = this.handleChange.bind(this);
    }
    handleClick(){
        this.setState(prevState => ({
            isToggleOn: !preveState.isToggleOn
        }));
    }
    handleChange(){
        console.log(this.state.isToggleOn);
    }
    render(){
        return(
           <button onClick={this.handleClick} onChange={this.handleChange}>
                {this.state.isToggleOn ? &#39;ON&#39;:&#39;OFF&#39;}
            </button>
        )
    }
}

构造方法中为什么要给所有的实例方法绑定this呢?

1. 代码执行的细节

上例仅仅是一个组件类的定义,当在其他组件中调用或是使用ReactDOM.render( )方法将其渲染到界面上时会生成一个组件的实例,因为组件是可以复用的,面向对象的编程方式非常适合它的定位。根据this指向的基本规则就可以知道,这里的this最终会指向组件的实例。

组件实例生成的时候,构造器constructor会被执行,此处着重分析一下下面这行代码:

this.handleClick = this.handleClick.bind(this);

此时的this指向新生成的实例,那么赋值语句右侧的表达式先查找this.handleClick( )这个方法,由对象的属性查找机制(沿原型链由近及远查找)可知此处会查找到原型方法this.handleClick( ),接着执行bind(this),此处的this指向新生成的实例,所以赋值语句右侧的表达式计算完成后,会生成一个指定了this的新方法,接着执行赋值操作,将新生成的函数赋值给实例的handleClick属性,由对象的赋值机制可知,此处的handleClick会直接作为实例属性生成。总结一下,上面的语句做了一件这样的事情:

把原型方法handleClick( )改变为实例方法handleClick( ),并且强制指定这个方法中的this指向当前的实例。

2. ES5的写法中为什么不用bind(this)?

ES5的写法是指使用React.createClass( )方法来定义组件,React在V16以上的新版本中已经移除了这个API,你可以通过阅读更早版本的源代码看到这个方法的细节。

  //旧版本`react`中`createClass`方法片段
  if (this.__reactAutoBindMap) {
        this._bindAutoBindMethods();
  }

在老版本的React中,createClass()的定义中可以看到上面的代码,抛开其他复杂的逻辑,从方法名就可以看出这是一个自动绑定的方法,实际上在这个方法中所完成的,就是对组件中自定义方法的this强制绑定,感兴趣的读者可以自行翻看源码了解细节。

3. 绑定this的必要性

在组件上绑定事件监听器,是为了响应用户的交互动作,特定的交互动作触发事件时,监听函数中往往都需要操作组件某个状态的值,进而对用户的点击行为提供响应反馈,对开发者来说,这个函数触发的时候,就需要能够拿到这个组件专属的状态合集(例如在上面的开关组件ToggleButton例子中,它的内部状态属性state.isToggleOn的值就标记了这个按钮应该显示ON或者OFF),所以此处强制绑定监听器函数的this指向当前实例的也很容易理解。

React构造方法中的bind会将响应函数与这个组件Component进行绑定以确保在这个处理函数中使用this时可以时刻指向这一组件的实例。

4. 如果不绑定this

如果类定义中没有绑定this的指向,当用户的点击动作触发this.handleClick( )这个方法时,实际上执行的是原型方法,可这样看起来并没有什么影响,如果当前组件的构造器中初始化了state这个属性,那么原型方法执行时,this.state会直接获取实例的state属性,如果构造其中没有初始化state这个属性(比如React中的UI组件),说明组件没有自身状态,此时即使调用原型方法似乎也没什么影响。

事实上的确是这样,这里的bind(this)所希望提前规避的,就是著名的this指针丢失的问题

例如使用解构赋值的方式获取某个属性方法时,就会造成引用转换丢失this的问题:

const toggleButton = new ToggleButton();

import {handleClick} = toggleButton;

上例中解构赋值获取到的handleClick这个方法在执行时就会报错,Class的内部是强制运行在严格模式下的,此处的this在赋值中丢失了原有的指向,在运行时指向了undefined,而undefined是没有属性的。

另一个存在的限制,是没有绑定this的响应函数在异步运行时可能会出问题,当它作为回调函数被传入一个异步执行的方法时,同样会因为丢失了thisrrreee

なぜこれをコンストラクター内のすべてのインスタンス メソッドにバインドする必要があるのですか方法? 🎜

1. コード実行の詳細

🎜 上記の例は、他のコンポーネントで呼び出される場合、または ReactDOM.render( ) を使用する場合のコンポーネント クラスの定義です。このメソッドは、コンポーネントをインターフェースにレンダリングするときにコンポーネントのインスタンスを生成します。コンポーネントは再利用できるため、オブジェクト指向プログラミング方法はその配置に非常に適しています。 this のポイントの基本ルールによれば、ここの this が最終的にコンポーネントのインスタンスをポイントすることがわかります。 🎜🎜コンポーネントのインスタンスが生成されると、コンストラクター constructor が実行されます。ここでは、次のコード行の分析に焦点を当てます: 🎜rrreee🎜この時点では、this がポイントしています。新しく生成されたインスタンスに代入すると、代入ステートメントの右側の式で、オブジェクトの属性検索メカニズムに従って、最初に this.handleClick( ) メソッドが検索されます (近くから遠くまで検索します)。プロトタイプ チェーン)、 がここにあることがわかります。strong>プロトタイプ メソッド <code>this.handleClick( ) を実行し、bind(this) を実行します。 >、this は新しく生成されたインスタンスを指すため、代入ステートメントの右側の式が計算された後、this を指定する新しいメソッドが生成されます。次に、オブジェクトの割り当てメカニズムである handleClick からわかるように、新しく生成された関数をインスタンスの handleClickプロトタイプ メソッド <code>handleClick( ) をインスタンス メソッド handleClick( ) に変更し、仕様 を強制します。このメソッドの >this は現在のインスタンスを指します。 🎜

2. ES5 の書き込みではbind(this) が使用されないのはなぜですか?

🎜 ES5 の書き込みメソッドでは、コンポーネントを定義するために React.createClass( ) メソッドを使用することを指します。 , Reactこの API は V16 以降の新しいバージョンでは削除されています。このメソッドの詳細は、以前のバージョンのソース コードを読むことで確認できます。 🎜rrreee🎜 React の古いバージョンでは、他の複雑なロジックはさておき、上記のコードは createClass() の定義で確認できます。これは自動バインド メソッドです。実際、このメソッドで行われるのは、コンポーネント内のカスタム メソッドに強制的にバインドされることです。興味のある方はソース コードを確認してください。詳細。 🎜

3.

🎜 コンポーネントにイベント リスナーをバインドする必要があるのは、特定の対話型アクションがイベントをトリガーするときに、リスニング関数がコンポーネントを操作する必要があることがよくあります。開発者は、この関数がトリガーされたときに、このコンポーネント専用の状態コレクションを取得できる必要があります (たとえば、スイッチ コンポーネント ToggleButton)。上例では、内部状態属性state.isToggleOnの値は、ボタンがオンまたはオフを表示することをマークします) , したがって、必須バインディングリスナー関数の this が現在のインスタンスを指していることが容易に理解できます。 🎜
🎜 React コンストラクターのバインドは、応答関数をこのコンポーネント Component にバインドし、このハンドラー関数でこれを使用するときに、常にこのコンポーネントのインスタンスを指すことができるようにします。 🎜

4. this

がバインドされていない場合 🎜 this のポインターがクラス定義でバインドされていない場合、this.handleClick はユーザーが ( ) をクリックすると、このメソッドは実際にプロトタイプ メソッドを実行しますが、stateが現在のコンポーネントのコンストラクターで初期化されている場合は影響を与えないようです。 >この属性は、プロトタイプ メソッドが実行されると、state 属性の場合、this.state によってインスタンスの state 属性を直接取得します。は構築内で初期化されていません (React の UI コンポーネントなど)。これは、コンポーネントが独自の状態を持たないことを示しており、現時点では、プロトタイプ メソッドを呼び出しても効果がないようです。 🎜🎜実際、これは実際に当てはまります。ここで bind(this) が事前に回避したいのは、 このポインタが見つからないという有名な問題です。 🎜🎜 たとえば、 特定の属性メソッドを取得するために構造化代入を使用すると、参照変換でこれが失われるという問題が発生します。 🎜rrreee🎜 上の例では、handleClick代入を構造化して取得したコード > このメソッドは実行時にエラーを報告します。ここで <code>this は、strict モードで実行されます。代入内の元のポインタが失われます。実行時には、unknown を指しますが、unknown には属性がありません。 🎜🎜もう 1 つの制限は、this をバインドしていない応答関数は、非同期実行メソッドにコールバック関数として渡されるときに問題を引き起こす可能性があることです。 code>this が失われ、エラーが発生します。 🎜

コンポーネント インスタンス メソッドに this の必須指定がない場合、参照変換を安全に使用したりコールバック関数として渡したりすることができません。将来の使用においてこの方法は、その後の使用や共同開発には不便です。 this,在将来的使用中就无法安心使用引用转换作为回调函数传递这样的方式,对于后续使用和协作开发而言都是不方便的。

5. 小结

this的使用非常灵活,但这种灵活性也带来了很多混乱。这里的bind(this)是为了改进javascript语言级的缺陷,并不是只有React中才需要这样做,这个问题是伴随着面向对象编程而产生的,在使用javascript进行插件和框架的开发时,这个问题的影响会更加明显。之所以说它是一个语言级的缺陷,是因为Java中对于this在同样场景下的指向更符合正常思维逻辑,而javascript

5. 概要

this は非常に柔軟に使用できますが、この柔軟性は多くの混乱も引き起こします。ここでの bind(this) は、JavaScript の言語レベルの欠陥を改善するためのものです。この問題は、javascript を使用する場合にのみ発生します。この問題の影響は、プラグインやフレームワークを開発するときにより明らかになります。これが言語レベルの欠陥であると言われる理由は、Java の同じシナリオでの this のポインティングは通常の思考ロジックにより一致しているためです。 code>javascript code>にバインディングが表示されていない場合、言語の演算結果とメソッドの名前が矛盾します。


関連する推奨事項:

React でアニメーションが有効にならない理由の分析

React の使用法: React コンポーネント内の状態管理

🎜

以上がReact コンポーネントでこれをバインドする理由の分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。