React キーを使用してコンポーネントの競合を回避する方法

React のアプローチは非常に複雑になる可能性があり、予期しない動作や、場合によっては微妙なバグが発生する可能性があります。原因に詳しくない場合、そのようなバグを取り除くことは非常に困難になる可能性があります。

特定のバグは、同じコンポーネントを異なるプロパティで条件付きでレンダリングする場合に発生します。このバグを詳細に調査し、React キーを使用して解決する方法を調べます。

React コンポーネントは常に独立しているわけではありません

そのわかりやすい構文は、React を学ぶべき主な理由の 1 つです。しかし、多くの利点にもかかわらず、このフレームワークはバグがないわけではありません。

ここで学ぶバグは、同じコンポーネントを条件付きでレンダリングするが、異なるプロパティを渡す場合に発生します。

このような場合、React は 2 つのコンポーネントが同じであると想定するため、2 番目のコンポーネントをレンダリングしません。その結果、最初のコンポーネントで定義した状態はレンダリング間で保持されます。

例を挙げて説明します。まず、次のCounterコンポーネントがあります。

import { useState, useEffect } from "react"export function Counter({name}) {const [count, setCount] = useState(0)return({name} setCount(c => c - 1)}> -  setCount(c => c + 1)}> + )}

このCounterコンポーネントは、オブジェクトの分解によって親からnameを受け取ります。これは React でプロパティを使用する方法です。次に、

に名前をレンダリングします。また、countを減らすためのボタンと、countを増やすためのボタンの 2 つのボタンを返します。

上記のコードには何も問題がないことに注意してください。バグは、カウンターを使用する次のコード ブロック (App コンポーネント) から発生します。

import { useState } from "react"import { Counter } from "./Counter"export default function App() {const [isKingsley, setIsKingsley] = useState(true)return({ isKingsley ?  :  } setIsKingsley(k => !k)}> Swap )}

デフォルトでは、上記のコードは Kingsley という名前のカウンターをレンダリングします。カウンターを 5 まで増やしてSwapボタンをクリックすると、Sally という名前の 2 番目のカウンターがレンダリングされます。

しかし、問題は、カウンターを交換した後、デフォルトの状態であるゼロにリセットされないことです。

このバグは、両方の状態が同じ要素を同じ順序でレンダリングするために発生します。React は「Kingsley」カウンターと「Sally」カウンターが異なることを知りません。唯一の違いはnameプロパティですが、残念ながら React は要素を区別するためにそれを使用しません。

この問題は 2 つの方法で回避できます。1 つ目は、DOM を変更して 2 つのツリーを異なるものにすることです。これには、DOM が何であるかを理解する必要があります。たとえば、最初のカウンターを

要素内にラップし、2 番目のカウンターを
要素内にラップすることができます。

import { useState } from "react"import { Counter } from "./Counter"export default function App() {const [isKingsley, setIsKingsley] = useState(true)return ({ isKingsley ?():()} setIsKingsley(k => !k)}> Swap )}

「Kingsley」カウンターを増やしてSwapをクリックすると、状態は 0 にリセットされます。これも、2 つの DOM ツリーの構造が異なるためです。

isKingsley変数がtrueの場合、構造はdiv>div>Counter (div が div を含み、div が Counter を含む) になります。ボタンを使用してカウンターの状態を入れ替えると、構造はdiv>section>Counterになります。この不一致により、React は自動的にリセットされた状態の新しい Counter をレンダリングします。

このようにマークアップの構造を常に変更したいとは限らない場合があります。このバグを解決する 2 番目の方法は、異なるマークアップの必要性を回避します。

キーを使用して新しいコンポーネントをレンダリングする

キーを使用すると、React はレンダリング プロセス中に要素を区別できます。したがって、まったく同じ 2 つの要素があり、一方を他方と異なるものであることを React に通知したい場合は、各要素に一意のキー属性を設定する必要があります。

次のように、各カウンターにキーを追加します。

import { useState } from "react"import { Counter } from "./Counter"export default function App() {const [isKingsley, setIsKingsley] = useState(true)return({ isKingsley ? :} setIsKingsley(k => !k)}> Swap )}

「Kingsley」カウンターを増やしてSwapをクリックすると、React は新しいカウンターをレンダリングし、状態をゼロにリセットします。

React は各項目の違いがわからないため、同じタイプの項目の配列をレンダリングするときにもキーを使用する必要があります。

export default function App() {const names = ["Kingsley", "John", "Ahmed"]return({ names.map((name, index) => {return })})}

キーを割り当てると、React は各項目に別々のカウンターを関連付けます。そのようにすることで、配列に加えた変更を反映させることができます。

キーの別の高度なユースケース

キーを使用して、要素を別の要素に関連付けることもできます。たとえば、状態変数の値に応じて、入力要素を異なる要素に関連付ける場合があります。

例として、App コンポーネントを微調整します。

import { useState } from "react"export default function App() {const [isKingsley, setIsKingsley] = useState(true)return({ isKingsley ? Kingsley's Score : Sally's score } setIsKingsley(k => !k)}> Swap )}

これで、Kingsley と Sally の

要素を切り替えるたびに、入力のキー属性が「Kingsley」と「Sally」の間で自動的に変更されます。これにより、ボタンをクリックするたびに React が入力要素を完全に再レンダリングすることになります。

React アプリケーションを最適化するためのヒント

コードの最適化は、Web アプリケーションやモバイル アプリケーションで快適なユーザー エクスペリエンスを作成するための鍵となります。さまざまな最適化手法について知っておくと、React アプリケーションを最大限に活用することができます。

最も良い点は、これらの最適化手法のほとんどを React Native アプリケーションにも適用できることです。

よくある質問

Q: React Native とは何ですか?

React Native は React を拡張して、クロスプラットフォーム アプリを開発するためのフレームワークを提供します。全体的な開発時間を大幅に短縮できます。

Q: コンポーネントを 100% 分離する必要がありますか?

React のコンポーネント システムはデータのカプセル化に役立ちますが、場合によってはバイパスする必要があります。状態をリフトアップすることは 1 つの方法ですが、プロップのドリルダウンにつながる可能性があり、それは欠点があります。

Q: プロップを使用して柔軟なコンポーネントを作成するにはどうすればよいですか?

手順に従って、カスタマイズにプロップを使用するサンプルの React 通知コンポーネントを作成できます。