配列を反復するときは注意してください!!
配列内の要素のインデックスを使用することは、おそらくおなじみのエラーを抑制するための許容できる方法であるというのは、よくある誤解です。
Each child in an array should have a unique "key" prop.
ただし、多くの場合はそうではありません。これは、状況によっては望ましくない動作につながるアンチパターンです。
key
小道具を理解する
Reactは、key
propを使用して、コンポーネントとDOM要素の関係を理解します。これは、調整プロセスに使用されます。したがって、鍵がことが非常に重要です常に残るユニークなそうでない要素をミックスし、間違ったものを変異させますリアクト良いチャンスがあり、。最高のパフォーマンスを維持するために、これらのキーがすべての再レンダリング全体で静的であることも重要です。
とはいえ、配列が完全に静的であることがわかっている場合は、必ずしも上記を適用する必要はありません。ただし、可能な限りベストプラクティスを適用することをお勧めします。
React開発者がこのGitHubの問題で言った:
- キーは実際にはパフォーマンスに関するものではなく、アイデンティティに関するものです(つまり、パフォーマンスの向上につながります)。ランダムに割り当てられ、変化する値は同一ではありません
- データがどのようにモデル化されているかを知らない限り、現実的にキーを[自動的に]提供することはできません。IDがない場合は、何らかのハッシュ関数を使用することをお勧めします
- 配列を使用するときにはすでに内部キーがありますが、それらは配列のインデックスです。新しい要素を挿入すると、それらのキーは正しくありません。
つまり、a key
は次のようになります。
- 一意 - 兄弟コンポーネントのキーと同じにすることはできません。
- 静的 -レンダリング間でキーを変更しないでください。
key
小道具の使用
上記の説明に従って、以下のサンプルを注意深く検討し、可能な場合は、推奨されるアプローチを実装してください。
悪い(潜在的に)
<tbody>
{rows.map((row, i) => {
return <ObjectRow key={i} />;
})}
</tbody>
これは間違いなく、Reactで配列を反復処理するときに見られる最も一般的な間違いです。このアプローチは技術的に「間違っている」わけではなく、単に... あなたが何をしているのかわからない場合は「危険」です。静的配列を反復処理する場合、これは完全に有効なアプローチです(たとえば、ナビゲーションメニューのリンクの配列)。ただし、アイテムを追加、削除、並べ替え、またはフィルタリングする場合は、注意が必要です。公式ドキュメントでこの詳細な説明をご覧ください。
class MyApp extends React.Component {
constructor() {
super();
this.state = {
arr: ["Item 1"]
}
}
click = () => {
this.setState({
arr: ['Item ' + (this.state.arr.length+1)].concat(this.state.arr),
});
}
render() {
return(
<div>
<button onClick={this.click}>Add</button>
<ul>
{this.state.arr.map(
(item, i) => <Item key={i} text={"Item " + i}>{item + " "}</Item>
)}
</ul>
</div>
);
}
}
const Item = (props) => {
return (
<li>
<label>{props.children}</label>
<input value={props.text} />
</li>
);
}
ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
このスニペットでは、非静的配列を使用しており、スタックとしての使用に限定していません。これは危険なアプローチです(理由は後で説明します)。配列の先頭にアイテムを追加すると(基本的にはシフトなし)、それぞれの値は<input>
そのまま残ります。どうして?key
は各アイテムを一意に識別しないためです。
つまり、最初Item 1
はを持っていkey={0}
ます。2番目のアイテムを追加すると、一番上のアイテムがになりItem 2
、Item 1
2番目のアイテムが続きます。しかし、今でItem 1
はもうkey={1}
ありませkey={0}
ん。代わりに、Item 2
今持っていますkey={0}
!!
そのため、Reactは<input>
、Item
withキー0
が常に一番上にあるため、要素は変更されていないと考えています。
なぜこのアプローチは時々悪いなのでしょうか?
この方法は、アレイが何らかの方法でフィルター処理、再配置、またはアイテムが追加/削除された場合にのみ危険です。常に静的である場合は、完全に安全に使用できます。たとえば、のようなナビゲーションメニュー["Home", "Products", "Contact us"]
は、新しいリンクを追加したり再配置したりすることがないため、このメソッドを使用して安全に反復できます。
つまり、次のようにしてインデックスを安全に使用できますkey
。
- 配列は静的であり、変更されることはありません。
- 配列はフィルタリングされません(配列のサブセットを表示します)。
- 配列は並べ替えられません。
- 配列はスタックまたはLIFOとして使用されます(後入れ先出し)。言い換えると、追加は配列の最後(つまり、プッシュ)でのみ実行でき、最後の項目のみを削除(つまり、ポップ)できます。
代わりに、上記のスニペットで、追加されたアイテムを配列の最後にプッシュした場合、既存の各アイテムの順序は常に正しくなります。
ひどい
<tbody>
{rows.map((row) => {
return <ObjectRow key={Math.random()} />;
})}
</tbody>
このアプローチはおそらくキーの一意性を保証しますが、これが必要でない場合でも、常にリスト内の各アイテムの再レンダリングを強制的に実行します。パフォーマンスに大きな影響を与えるため、これは非常に悪いソリューションです。言うまでもなくMath.random()
、同じ番号が2回生成されるイベントでキーが衝突する可能性を排除することはできません。
不安定なキー(によって生成されるものなどMath.random()
)は、多くのコンポーネントインスタンスとDOMノードを不必要に再作成し、パフォーマンスの低下と子コンポーネントの状態の損失を引き起こす可能性があります。
とても良い
<tbody>
{rows.map((row) => {
return <ObjectRow key={row.uniqueId} />;
})}
</tbody>
これは、データセット内のアイテムごとに一意のプロパティを使用するため、間違いなく最良のアプローチです。たとえばrows
、データベースからフェッチされたデータが含まれている場合は、テーブルの主キー(通常は自動インクリメントされる数値)を使用できます。
キーを選択する最良の方法は、兄弟間でリストアイテムを一意に識別する文字列を使用することです。ほとんどの場合、データのIDをキーとして使用します
良い
componentWillMount() {
let rows = this.props.rows.map(item => {
return {uid: SomeLibrary.generateUniqueID(), value: item};
});
}
...
<tbody>
{rows.map((row) => {
return <ObjectRow key={row.uid} />;
})}
</tbody>
これも良い方法です。データセットに一意性を保証するデータ(任意の数値の配列など)が含まれていない場合、キーが衝突する可能性があります。このような場合、データセットを反復処理する前に、データセット内の各アイテムに一意の識別子を手動で生成することをお勧めします。コンポーネントを再レンダリングするたびにではなく、一度だけこれを行うために、コンポーネントをマウントするとき、またはデータセットを受信するとき(非同期API呼び出しからprops
または非同期API呼び出しなど)が望ましいです。このようなキーを提供できるライブラリはすでにいくつかあります。次に例を示します。react-key-index。
key
プロパティが必要です。これは、ReactJSが適切なDOMノードへの参照を見つけ、マークアップ内のコンテンツのみを更新し、テーブル/行全体を再レンダリングしないようにするのに役立ちます。