Reactのref属性を深く理解する方法

Reactのref属性を深く理解する方法

概要

まず、Refs と ref は 2 つの概念です。Refs は、特定の API を使用して作成できる React によって提供されるオブジェクトです。このオブジェクトの構造は次のとおりです。

このオブジェクトには、現在の属性が 1 つだけあります。では、このオブジェクトは何に使用されるのでしょうか?

Ref を使用すると、render メソッドで作成された DOM ノードまたは React 要素にアクセスできます。 (DOM ノードはネイティブ DOM 要素を参照し、render() メソッドで作成された React 要素は React クラスのコンポーネント要素を参照します)

このような要件は、2 つの兄弟要素 (1 つは div、もう 1 つはボタン) を想像することができます。ここで、ボタンをクリックして div の背景色を変更します。ネイティブ DOM テクノロジーでは、ボタン クリック関数で document.querySelector('xxx') を使用して div ノードを選択し、その背景スタイルを変更できます。ただし、Vue や React などのフレームワークの場合でも、ページ要素は動的に生成されるため、DOM API を使用して取得することはできません。さらに、React で操作される要素のほとんどはネイティブ DOM 要素ではなく、React 要素です。 では、ネイティブ DOM 要素または React 要素をどのように選択するのでしょうか?

実際、理論的には、フロントエンド フレームワークにおけるコンポーネントの独立性の概念が失われるため、選択操作を実行する必要はありません。通常、イベントはコンポーネント通信を通じて処理されます。上記の状況では、EventBus を使用してコンポーネントと通信できます。ボタンのクリック イベントでカスタム イベントをトリガーし、div でカスタム イベントをリッスンして、ボタン イベントで操作する div を直接取得するのではなく、イベント通知の形式でボタンが div に背景色の変更を通知できるようにします。

しかし、React は、Ref を通じて DOM 要素と React 要素に直接アクセスする方法を提供します。使い方は非常に簡単で、アクセスしたい要素に ref 属性を追加し、Refs オブジェクトを ref 属性にアタッチします。すると、Refs オブジェクトの現在の属性は空ではなくなり、対応する DOM 要素または React 要素のインスタンスになります。

1. Refsオブジェクトの作成

React では、Ref オブジェクトを作成する方法が 2 つあります。

1.1 React.createRef()

React.createRef() は Ref オブジェクトを作成します。このオブジェクトは ref 属性に添付することでアクセスできます。
このメソッドは、関数コンポーネントとクラスコンポーネントの両方で使用できます。

1.2 React.useRef(初期値)

React 16.8 でフックを追加した後、Ref オブジェクトを作成できる別のフックがあります。つまり、React.useRef(initialValue) です。
useRef は、渡された引数 (initialValue) に .current プロパティが初期化された変更可能な ref オブジェクトを返します。返された ref オブジェクトは、コンポーネントのライフサイクル全体を通じて変更されません。
このメソッドは関数コンポーネントでのみ使用できます。

2. ref属性の使用

ref 属性は、ネイティブ DOM 要素または React クラス コンポーネントにのみ追加できます。関数コンポーネントにはインスタンスがないため、関数コンポーネントでは ref 属性を使用できません。

関数コンポーネントで ref 属性を使用する場合は、React.forwardRef を介して関数コンポーネント内のネイティブ DOM 要素に Ref を転送できます。

2.1 ネイティブDOM要素にrefを追加する

クラスコンポーネント内

クラスAppはReact.Componentを拡張します。
    コンストラクタ(props){
        スーパー(小道具)
        this.myRef = React.createRef()
    } 
    コンポーネントマウント(){
        コンソールログ(this.myRef)
        コンソールログ(this.myRef.current)
    }
    与える(){
        戻る (
            <div ref={this.myRef}>私はアプリコンポーネントです</div>
        )
    }
}

関数コンポーネント内

定数App = ()=>{
    定数 myRef = React.useRef(null)
    //const myRef = React.createRef() refオブジェクトを作成する方法はどちらもReact.useEffect(()=>{
        コンソールログ(myRef)
        コンソールログ(myRef.current)
    },[]) //シミュレーションライフサイクル return (
        <div ref={myRef}>関数コンポーネント内で ref を使用する div です</div>
    )
}

2.2 クラスコンポーネントにrefを追加する

クラスClassChildはReact.Componentを拡張します{
    与える(){
        戻る (
            <div>私は App コンポーネントのクラス子コンポーネント ClassChild です</div>
        )
    }
}

クラスAppはReact.Componentを拡張します。
    コンストラクタ(props){
        スーパー(小道具)
        this.myRef = React.createRef()
    } 
    コンポーネントマウント(){
        コンソールログ(this.myRef)
        コンソールログ(this.myRef.current)
    }
    与える(){
        戻る (
            <ClassChild ref={this.myRef}/>
        )
    }
}

2.3 クラスコンポーネントによって転送されたネイティブDOM要素にrefを追加する

ref 転送の原則は、親コンポーネントで定義された ref オブジェクトを通常の属性として子コンポーネントに渡し、子コンポーネントが props を通じてそれを受け取り、独自の DOM 要素に割り当てることです。

クラスClassChildはReact.Componentを拡張します{
    与える(){
        戻る (
            <div ref={this.props.refProp}>私はAppコンポーネントのクラス子コンポーネントClassChildです</div> //refを追加しました
        )
    }
}

クラスAppはReact.Componentを拡張します。
    コンストラクタ(props){
        スーパー(小道具)
        this.myRef = React.createRef()
    } 
    コンポーネントマウント(){
        コンソールログ(this.myRef)
        コンソールログ(this.myRef.current)
    }
    与える(){
        戻る (
            <ClassChild refProp={this.myRef}/> // 通常のプロパティとして渡されます)
    }
}

2.4 関数コンポーネントによって転送されたネイティブDOM要素への参照を追加する

クラスコンポーネント転送の原則に従って、私が考えた実装方法は次のとおりです。

const FunChild = (props)=>{
    戻る (
        <div ref={props.refProp}>私は関数コンポーネント FunChild です</div>
    )
}
クラスAppはReact.Componentを拡張します。
    コンストラクタ(props){
        スーパー(小道具)
        this.myRef = React.createRef()
    } 
    コンポーネントマウント(){
        コンソールログ(this.myRef)
        コンソールログ(this.myRef.current)
    }
    与える(){
        戻る (
            <FunChild refProp={this.myRef}/>
        )
    }
}

この実装は可能ですが、関数コンポーネントで直接 ref 属性を使用する方法ではありません。React は、関数コンポーネントで直接 ref を使用する方法、つまり React.forwardRef を使用して React 要素を作成する方法を提供します。

React.forwardRef

const FunChild = React.forwardRef((props, ref)=>{
    戻る (
        <div ref={ref}>私は関数コンポーネント FunChild です</div>
    )
}) // React.forwardRef を使用して関数コンポーネントを変換します。class App extends React.Component{
    コンストラクタ(props){
        スーパー(小道具)
        this.myRef = React.createRef()
    } 
    コンポーネントマウント(){
        コンソールログ(this.myRef)
        コンソールログ(this.myRef.current)
    }
    与える(){
        戻る (
            <FunChild ref={this.myRef}/> // ref を関数コンポーネントに直接渡す
        )
    }
}

React.forwardRef は props から ref 属性だけを抽出するだけのように思えます。
上記の方法では関数コンポーネントで ref 属性の使用を実装していますが、この時点での Ref オブジェクトは、アクセスされている関数コンポーネント内のネイティブ DOM 要素またはその他のクラス コンポーネントです。つまり、ここでの関数コンポーネントは転送の役割のみを果たします。

3. コールバック参照

上記の方法では、Ref オブジェクトを作成し、ref 属性を介してネイティブ DOM 要素またはクラス コンポーネントにマウントして、要素またはインスタンスにアクセスします。
実際、ref 属性は Ref オブジェクトに加えてコールバック関数を受け取ることができます。
ref 属性が Ref オブジェクトを受け取ると、対応する DOM 要素またはクラス コンポーネント インスタンスが Ref オブジェクト内の現在の属性に直接割り当てられます。 ref 属性がコールバック関数を受け取ると、対応する DOM 要素またはクラス コンポーネント インスタンスがコールバック関数のパラメーターとして渡され、コールバック関数が呼び出されます。
したがって、Ref オブジェクトに依存せずにコールバック Ref を使用して、アクセスする要素またはインスタンスをより柔軟に制御できます。

クラスAppはReact.Componentを拡張します。
    コンストラクタ(props){
        スーパー(小道具)
        this.myRef = null
        this.setMyRef = (要素)=>{
            this.myRef = 要素
        }
    } 
    コンポーネントマウント(){
        コンソールログ(this.myRef)
    }
    与える(){
        戻る (
            <div ref={this.setMyRef}>私はアプリコンポーネントです</div>
        )
    }
}

以上がReactのref属性を深く理解する方法の詳細です。Reactのref属性を深く理解する方法の詳細については、123WORDPRESS.COMの他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • Reactの3つのコア特性の深い理解
  • React の 3 つの主要属性における Ref の使用に関する詳細な説明
  • Reactの3つの主要属性におけるpropsの使用の詳細な説明
  • Reactの3つの主要属性における状態の使用の詳細な説明
  • React の 3 つの主要な特性をご存知ですか?

<<:  Nginx プロキシ axios リクエストと注意事項

>>:  CentOS に MySQL 8.0 をインストールして設定するための詳細な手順

推薦する

MySQL 5.7.30 のインストールとアップグレードの問題に関する詳細なチュートリアル

くさびコンピュータにインストールされている MySQL のバージョンが比較的古く、おそらくバージョン...

階段効果を実現するためのWeChatアプレットカスタムメニューナビゲーション

設計意図ページを開発する際には、ページ上のナビゲーション メニューをクリックしたときにページを対応す...

MySQL実行計画の詳細な説明

EXPLAIN ステートメントは、MySQL がステートメントを実行する方法に関する情報を提供します...

血の写輪眼と輪廻眼の特殊効果コードを実現するためのHTML+CSS

結果 (完全なコードは下部にあります): 実装は難しくありませんが、繰り返しコードが多くなります。実...

VMware 仮想マシンのインストール Linux システムのグラフィック チュートリアル

この記事では、LinuxシステムのVMwareインストールの具体的な手順を参考までに紹介します。具体...

JavaScriptの記事では、Webフォームの操作方法を説明します。

1. はじめに先ほど、ウェブページの急速な発展について紹介しました。今回は、より深い内容についてお...

MySQLにインデックスを追加しても効果がないいくつかの状況について簡単に説明します。

インデックスを追加すると、クエリの効率が向上します。インデックスを追加するということは、ドキュメント...

vuex での mapState の考え方の応用

目次1. マップ方式2. 応用背景:需要開発プロセス中に、一部のインターフェースは、ページに表示する...

MySQLからデータをインポートする際の不正なフォーマット、インポートの遅延、データ損失などの問題を迅速に解決します。

遅い問題を完全に解決したい場合は、MySQL を MySQL 8.0 にアップグレードすることをお勧...

TypeScriptにおけるunknownとanyの違いについて詳しく説明します

目次序文1. 不明 vs 任意2. 未知とあらゆるもののメンタルモデル3. まとめ要約する序文any...

両端の CSS レイアウトのサンプルコード (親の負のマージンを使用)

最近、開発中に両端が揃ったレイアウトに遭遇しました。レイアウトはパーセンテージに基づいていました。以...

JS で美しい条件式を書く方法についての簡単な説明

目次複数の条件文複数属性オブジェクトスイッチステートメントを置き換えるデフォルトパラメータとデストラ...

MySQL 5.7 で業務を停止せずに従来のレプリケーションを GTID レプリケーションに変更する例

GTID の利点により、従来のファイル POS ベースのレプリケーションを GTID ベースのレプリ...

WeChatアプレットが計算機機能を実装

この記事では、WeChatアプレットの計算機機能を実装するための具体的なコードを参考までに紹介します...

Ubuntu LinuxにOracle Java 14をインストールする方法

最近、Oracle は Java 14 (または Oracle JDK 14) の一般公開を発表しま...