Reactにおけるキーの役割の詳細な説明

Reactにおけるキーの役割の詳細な説明

React におけるキーの役割を理解するには、まずキーの値から始めます。キーの値は、不定値、インデックス値、明確で一意の値の 3 種類に分けられます。

次のコードでは、キーの値は不定値です(Math.random())

質問: ボタンをクリックすると、スパンの色が赤に変わりますか?

React をインポートし、{useState} を 'react' から取得します。

関数App() {
  定数[initMap、setInitMap] = useState([1,2,3,4]);
  const ハンドルクリック = () => {
    setInitMap([1,2,3,4])
    var spanEle = document.getElementsByTagName('span');
    Array.from(spanEle).map(it => it.style.color = 'red')
  }
  
  戻る (
    <div className="アプリ" id="アプリ">
      {
        initMap.map((it,index) => <div key={Math.random()}><span>色</span></div>)
      }
      <ボタンのonClick={() => handleClick()}></ボタン>
    </div>
  );
}

デフォルトのアプリをエクスポートします。

答えは「いいえ」です

この問題は、Reactレンダリングメカニズムとdiffアルゴリズムに関係しています。

公式ウェブサイトでは、diff に関して次のようなルールが定められています。

  • 要素タイプが変更されると、破棄されて再構築されます
  • 要素タイプが変更されていない場合は、属性を比較します
  • コンポーネント要素の型が変更されていない場合は、propsを通じて子ノードを再帰的に決定します。
  • 子ノードを再帰的に比較します。子ノードがリストの場合は、キーとプロパティで判断します。キーが一貫している場合は更新し、キーが一貫していない場合は破棄して再構築します。

上記の問題を分析します。

ボタンをクリックするとsetInitMap([1,2,3,4])によりレンダリングが発生します。レンダリング時に新しい仮想DOMが生成されます。ただし、この時に取得するspan要素は前の要素であるため(setInitMapは非同期で実行されるため)、新旧DOMの比較が行われます。

コード initMap.map((it,index) => <div key={Math.random()}><span>color</span></div>)

ここでの div はリストです。4 番目の diff ルールと比較して、React はキーに基づいて実際の DOM を更新するかどうかを決定します。 key={Math.random()}の場合、新旧のDOMの値が一致しない場合は、divが再生成されます。更新前に要素に赤いスタイルを追加したので、再作成された要素にはこのスタイルは適用されません。効果は次のようになります。

2番目のケース: キー値がインデックス値である

上記の分析の結果、キーの変更により、レンダリング時に div 要素が再生成されることがわかりました。レンダリングの前後でキーが変更されない場合はどうなるでしょうか?たとえば、キーをインデックスに変更する

質問: このコードでボタンをクリックすると、スパンの色は変わりますか?

戻る (
    <div className="アプリ" id="アプリ">
      <スピン 回転 = {スピン}></スピン>
      {
        initMap.map((it,index) => <div key={index}><span>色</span></div>)
      }
      <ボタンのonClick={() => handleClick()}></ボタン>
    </div>
  );

回答: はい

分析: レンダリングの前後でインデックスが変更されないため、div は再生成されません。次に、span 要素を比較します。span 要素の属性はレンダリングの前後で変更されるため、React は span 要素に新しい属性のみを適用しますが、それらは依然として前の要素を指します。

3 番目のケース: キー値が決定され、一意です。

この例では、キーをindexに設定することで、spanの色が変わりますが、キーを使用する場合、React公式サイトではindexの使用は推奨されていません。

上記のコードを修正する

  定数[initMap、setInitMap] = useState([1,2,3,4]);
  const ハンドルクリック = () => {
    初期化マップを設定します([3,2,1,4])
  }
  戻る (
    <div className="アプリ" id="アプリ">
      {
        initMap.map((it,index) => <div key={index}><input type="radio" />{it}</div>)
      }
      <button onClick={() => handleClick()}>クリック</button>
    </div>
  );
}

初期化時に値3のボタンを選択します

ボタンをクリックしてください

期待される効果は、値が 3 のボタンがまだ選択されているが、値が 1 のボタンになることです。

分析:

  1. setStateはレンダリングを引き起こします
  2. divのインデックスは変更されないため、divは再生成されず、入力は状態とpropsによって制御されないため、要素の状態は変更されません。
  3. 唯一変わるのは、状態によって影響を受けることです

望ましい効果を達成したい場合は、ユニークで確実なキーを設定する必要があります

テスト1:

{
   initMap.map((it) => <div キー = {it}><input タイプ = "radio" />{it}</div>)
}

初期化中に3番目のボタンを選択します

ボタンをクリックしてください

これは期待される効果です

考えてみてください。キーを Math.random() に設定するとどのような効果があるでしょうか?ボタンの状態は保存されますか?

クリックする前に:

クリック後:

無線状態は保存されません。

上記の例を通して、Reactにおけるキーの役割を理解できたと思います。以下の内容はReactの知識ポイントの拡張です。

拡張コンテンツ: 記事の冒頭のコードには、React の他の 2 つの知識ポイントも含まれています。1 つは前述の React のレンダリング条件であり、もう 1 つは実際の DOM の操作です。

拡張1: Reactレンダリング条件

'./App.css' をインポートします。
React をインポートし、{useState} を 'react' から取得します。

関数App() {
  定数[initMap、setInitMap] = useState([1,2,3,4]);
  const [spin, setSpin] = useState(false);
  const ハンドルクリック = () => {
    setSpin(true); //変更された部分 var spanEle = document.getElementsByTagName('span');
    Array.from(spanEle).map(it => it.style.color = 'red')
    setSpin(false); //パーツを変更}
  
  戻る (
    <div className="アプリ" id="アプリ">
      <スピン 回転 = {スピン}></スピン>
      {
        initMap.map((it,index) => <div key={Math.random()}><span>{it}</span></div>)
      }
      <ボタンのonClick={() => handleClick()}></ボタン>
    </div>
  );
}

デフォルトのアプリをエクスポートします。

クリック前のテスト結果は次のとおりです。

クリック後:

このコードでは、div のキーは引き続き Math.random() を使用していますが、initMap の状態は変更されていないため、再レンダリングされません。このとき、div は破棄されずに再構築されます。

拡張 2: 実際の DOM を操作することは可能ですか?

React では、実際の DOM 要素はより複雑なオブジェクトであり、操作には多くの計算が必要になるため、仮想 DOM の登場により実際の DOM に対する操作が削減されます。上記のコードでは、DOM ノードを直接操作してスタイルを変更していますが、これはお勧めできません。 React は状態とプロパティの変更に基づいてページをレンダリングするため、状態を通じてページのレンダリングを制御する方が適切です。

変更されたコードは次のとおりです。

関数App() {
  定数[initMap、setInitMap] = useState([1,2,3,4]);
  const [spin, setSpin] = useState(false);
  const [showColor, setShowColor] = useState(false);
  const ハンドルクリック = () => {
    setInitMap([3,2,1,4]);
    ShowColor を true に設定します。
  }
  
  戻る (
    <div className="アプリ" id="アプリ">
      <スピンスピン={スピン}>
      {
        initMap.map((it,index) => <div key={Math.random()}><span className={showColor && 'span-color'}>色</span></div>)
      }
      </スピン>
      <button onClick={() => handleClick()}>クリック</button>
    </div>
  );
}

このとき、spanは制御されたコンポーネントであり、要素のレンダリングはshowColorの状態によって制御できます。

クリックする前に:

クリック後:

状態を使用してレンダリングを制御すると、コードの量が削減され、結果が期待どおりになります。

要約する

  1. キーを使用する場合は、キーが一意かつ決定論的であることを確認してください。キー値が Math.random() の場合、コンポーネントが再構築され、要素に対する以前の操作が無効になる可能性があります。
  2. ページをレンダリングするときは、実際のDOMを操作せず、状態を使用してページを更新するようにしてください。

以上がReactにおけるキーの役割についての詳しい説明です。Reactキーの役割についてさらに詳しく知りたい方は、123WORDPRESS.COMの他の関連記事もぜひご覧ください!

以下もご興味があるかもしれません:
  • React Native が「NSArray<id<RCTBridgeModule>>型のパラメータを初期化できません」というエラーを報告する (解決方法)
  • Reactの基本のまとめ
  • Reactの親コンポーネントと子コンポーネント間のデータ転送の詳細な説明
  • React 入門レベルの詳細なメモ

<<:  nginx プロキシ サーバーで双方向証明書検証を構成する方法

>>:  Nginx の add_header ディレクティブに注意する必要があるのはなぜですか?

推薦する

MySQL のテーブル内のレコード数を制限する方法

目次1. トリガーソリューション2. パーティションテーブルソリューション3. 一般的な表領域ソリュ...

写真のプレビューとアップロード機能を実現するhtml+css+js

はじめに: Web ページを作成するときに、画像をアップロードする必要がある場合がよくあります。画像...

nginx を使用して 1 つのドメイン名で複数の Laravel プロジェクトを構成する方法の例

背景会社のサブプロジェクトが増えるにつれて、さまざまなサイズのプロジェクトが10個以上になります(バ...

Linux 上で Docker コンテナを作成、一覧表示、削除する方法の概要

1. Dockerコンテナを起動する以下のコマンドを使用して新しい Docker コンテナを起動しま...

Ubuntu 16.04 で PostgreSQL の起動を設定する方法

PostgreSQL はコンパイルされインストールされるため、起動時に起動するように設定する必要があ...

完全バックアップとポイントインタイムバックアップにmysqldumpを使用する方法

Mysqldump は MySQL の論理バックアップに使用されます。高速ではありませんが、柔軟性が...

CSS3 タイムラインアニメーション

成果を達成する html <h2>CSS3 タイムライン</h2> <...

デジタルテーブル特殊効果を実現するネイティブJS

この記事では、ネイティブ JS で実装されたデジタル時計エフェクトを紹介します。エフェクトは次のとお...

MySQLデータベースのパスワードを忘れた場合の解決策

先ほど MySQL パスワードを設定したのに、外食したり荷物を受け取ったりするときにパスワードを忘れ...

Linux のさまざまなロックメカニズムの使用方法と違いについて詳しく説明します

序文:この知識を理解する必要がある人は、すでにプロセス間通信とスレッド間通信の基本的な理解を持ってい...

Vue3 での Teleport の使用に関する詳細な説明

目次テレポートの目的テレポートの仕組みこの記事では、以下の内容を取り上げます。テレポートの目的テレポ...

InnoDB タイプの MySql によるテーブル構造とデータの復元

前提条件: データベースを復元するために必要な .frm ファイルと .ibd ファイルを保存します...

Docker 接続 MongoDB 実装プロセスとコード例

コンテナが起動した後まず管理者にログインして新しいユーザーを作成してください $ docker ex...

alpineをベースにdockerfileで作成したクローラーScrapyイメージの実装

1.アルパインイメージをダウンロードする [root@DockerBrian ~]# docker ...

モバイル開発におけるHTML5開発の現状を深く理解する

「私たちは次の一連のモバイル製品を HTML5 で作成しています。」 「ええ、最近は多くの人が Ap...