Reactで何かのリスト(arrayの中の要素など)をレンダーする際にkey props
をつけ忘れると、warningで怒られた、という経験のある人もいるのではないでしょうか。そして、なんとなくkeyをつけておしまい、という人が多いような気がします(過去の僕も含め。)
この記事で「なぜReactではkey props
を加える必要があるのか」を、まとめます。結論から言えば、key props
はパフォーマンスに影響してきます!
Contents
なぜ Reactを使う時にKey props が必要なのか?
一言で言えば、 React の “差分” アルゴリズムの処理に利用されるからです。
それでは、具体例を交えて説明していきます。
子要素の差分検出のプロセス
Reactは、デフォルトの状態(keyをつけていない状態)では、変更前と変更後の両方の子要素リストのそれぞれ先頭の要素から(上から)同時に処理を進め、差分を見つけたところで更新を発生させます。
Key propsを加えなくても特に問題にならないケース
まずは、key props
がなくても問題ないケースをみていきます。下記の例では、上から順番にまず <li>アイテム1</li>
を比較、<li>アイテム2</li>
を比較、そして<li>アイテム3</li>
に到達したところで、差分があると検出し、 <li>アイテム3</li>
のみDOMに追加せよと指示を出します。つまり、この例では、key propsを渡してなくても、特にパフォーマンスに問題は生じません。
変更前の仮想DOM
<ul> <li>アイテム1</li> <li>アイテム2</li> </ul>
変更後の仮想DOM
<ul> <li>アイテム1</li> <li>アイテム2</li> <li>アイテム3</li> </ul>
Key propsがないとパフォーマンスに影響が出るケース
次は、key props
ないとパフォーマンスに問題が出るケースをみていきます。
下記の例では、小要素の先頭に <li>セルジオ</li>
さんが追加されています。
key propsのない状態では、先程の例と同様に、上から順番に比較するので、下記のようになります。
-
<li>本田</li>
と<li>セルジオ</li>
を比較 =>更新 -
<li>久保</li>
と<li>本田</li>
を比較 =>更新 -
<li>久保</li>
を追加の要素と認識 =>更新
つまり、全ての小要素に更新が必要だと判断してしまいます。
このケースでは、実際には
<li>本田</li>
<li>久保</li>
は、更新の必要がないのに、更新しています。
変更前の仮想DOM
<ul> <li>本田</li> <li>久保</li> </ul>
変更後の仮想DOM
<ul> <li>セルジオ</li> <li>本田</li> <li>久保</li> </ul>
ここで、key props の出番です!
次のセクションで、この例にkey propsを追加してみましょう。
Key propsがないとパフォーマンスに影響が出るケースにkeyを追加する
Reactはkey属性を見つけると、それを元に小要素の比較を行うことができます。デフォルトでは先頭から順番に比較するだけでしたが、今はkey props
があるので、対応するkeyを検出し、<li key=“4”>本田</li>
と<li key=“17”>久保</li>
は、移動しただけで、<li key=“10”>セルジオ</li>
が新たに追加された子要素であると判断できます。これにより、先ほどの無駄な子要素の更新を防ぐことができます。
変更前の仮想DOM
<ul> <li key=“4”>本田</li> <li key=“17”>久保</li> </ul>
変更後の仮想DOM
<ul> <li key=“10”>セルジオ</li> <li key=“4”>本田</li> <li key=“17”>久保</li> </ul>
React Key props を実装する時のポイント
- 表示しようとしている要素が既に固有の ID を持っていれば、そのIDをkeyにすれば良い。
key
は兄弟要素間でユニークであれば良くて、グローバルでユニークである必要はない。- 配列の要素のインデックスを key として渡すことができるが、ソートなどの要素の並び替えがある場合にはパフォーマンスが低下する。よって、インデックスを安易にkeyに設定するのは避けるべき。
- 不安定な key(例えば
Math.random()
関数で生成するような)は多くのコンポーネントのインスタンスと DOM ノードを不必要に再生成し、パフォーマンスの低下や子コンポーネントの state の喪失を引き起こします。