React.jsの配列の子の一意のキーを理解する


656

JSONデータソースを受け入れ、並べ替え可能なテーブルを作成するReactコンポーネントを構築しています。
各動的データ行には一意のキーが割り当てられていますが、次のエラーが発生します。

配列の各子には、一意の「キー」プロップが必要です。
TableComponentのrenderメソッドを確認してください。

私のTableComponentrenderメソッドは次を返します:

<table>
  <thead key="thead">
    <TableHeader columns={columnNames}/>
  </thead>
  <tbody key="tbody">
    { rows }
  </tbody>
</table>

TableHeaderコンポーネントは、単一の行であり、また、それに割り当てられた一意のキーを有しています。

rowrows、一意のキーを持つコンポーネントから構築されています。

<TableRowItem key={item.id} data={item} columns={columnNames}/>

そして、TableRowItemこのように見えます:

var TableRowItem = React.createClass({
  render: function() {

    var td = function() {
        return this.props.columns.map(function(c) {
          return <td key={this.props.data[c]}>{this.props.data[c]}</td>;
        }, this);
      }.bind(this);

    return (
      <tr>{ td(this.props.item) }</tr>
    )
  }
});

ユニークキープロップエラーの原因は何ですか?


7
JS配列の行には一意のkeyプロパティが必要です。これは、ReactJSが適切なDOMノードへの参照を見つけ、マークアップ内のコンテンツのみを更新し、テーブル/行全体を再レンダリングしないようにするのに役立ちます。
キリル

rows配列、できればjsfiddle も共有できますか?あなたはdont必要keyにプロパティをtheadし、tbody道で。
nilgun 2015

行コンポーネントを元の質問@nilgunに追加しました。
Brett DeWoody、2015

3
一部のアイテムにIDがないか、同じIDを持つ可能性はありますか?
nilgun 2015

回答:


623

それぞれの子だけでなく、内の各要素にもキーを追加する必要があります。

このようにして、Reactは最小限のDOM変更を処理できます。

あなたのコードでは、それぞれ<TableRowItem key={item.id} data={item} columns={columnNames}/>がキーなしでそれらの中にいくつかの子供をレンダリングしようとしています。

この例を確認してください。

div内key={i}<b></b>要素からを削除してみてください(コンソールを確認してください)。

サンプルで、<b>要素にキーを指定せず、のみを更新したい場合object.city、単に要素対再レンダリング行全体へのニーズが反応します。

これがコードです:

var data = [{name:'Jhon', age:28, city:'HO'},
            {name:'Onhj', age:82, city:'HN'},
            {name:'Nohj', age:41, city:'IT'}
           ];

var Hello = React.createClass({

    render: function() {

      var _data = this.props.info;
      console.log(_data);
      return(
        <div>
            {_data.map(function(object, i){
               return <div className={"row"} key={i}> 
                          {[ object.name ,
                             // remove the key
                             <b className="fosfo" key={i}> {object.city} </b> , 
                             object.age
                          ]}
                      </div>; 
             })}
        </div>
       );
    }
});

React.render(<Hello info={data} />, document.body);

下部にある@Chrisによって投稿された回答は、この回答よりもはるかに詳細です。見てくださいhttps://stackoverflow.com/a/43892905/2325522を

調整におけるキーの重要性に関するReact文書:キー


5
まったく同じエラーが発生しています。これはチャット後に解決されましたか?もしそうなら、この質問の更新を投稿していただけませんか。
Deke

1
答えは機能します、デケ。keyプロップの値がアイテムごとに一意であることを確認しkey、配列の境界に最も近いコンポーネントにプロップを配置します。たとえば、React Nativeでは、最初にコンポーネントにkey小道具を配置しようとしました<Text>。ただし、の<View>親であるコンポーネントに配置する必要がありました<Text>。Array == [(View> Text)、(View> Text)]の場合、それをビューに配置する必要があります。テキストではありません。
scaryguy

240
Reactが独自のキーを生成することがそれほど難しいのはなぜですか?
Davor Lucic

13
@DavorLucic、ここにディスカッションがあります:github.com/facebook/react/issues/1342#issuecomment-39230939
koddo

5
これは、上記にリンクされた問題のチャットで作成されたメモのほぼ公式な言葉です。キーは、セットのメンバーのIDに関するものであり、任意のイテレーターから出現するアイテムのキーの自動生成は、おそらくReact内のパフォーマンスに影響します。図書館。
1

523

配列を反復するときは注意してください!!

配列内の要素のインデックスを使用することは、おそらくおなじみのエラーを抑制するための許容できる方法であるというのは、よくある誤解です。

Each child in an array should have a unique "key" prop.

ただし、多くの場合はそうではありません。これは状況によっては望ましくない動作につながるアンチパターンです。


key小道具を理解する

Reactは、keypropを使用して、コンポーネントとDOM要素の関係を理解します。これは、調整プロセスに使用されます。したがって、鍵がことが非常に重要です常に残るユニークなそうでない要素をミックスし、間違ったものを変異させますリアクト良いチャンスがあり、。最高のパフォーマンスを維持するために、これらのキーすべての再レンダリング全体で静的であることも重要です。

とはいえ、配列が完全に静的であることがわかっている場合は、必ずしも上記を適用する必要はありません。ただし、可能な限りベストプラクティスを適用することをお勧めします。

React開発者がこのGitHubの問題で言った:

  • キーは実際にはパフォーマンスに関するものではなく、アイデンティティに関するものです(つまり、パフォーマンスの向上につながります)。ランダムに割り当てられ、変化する値は同一ではありません
  • データがどのようにモデル化されているかを知らない限り、現実的にキーを[自動的に]提供することはできません。IDがない場合は、何らかのハッシュ関数を使用することをお勧めします
  • 配列を使用するときにはすでに内部キーがありますが、それらは配列のインデックスです。新しい要素を挿入すると、それらのキーは正しくありません。

つまり、a keyは次のようになります。

  • 一意 - 兄弟コンポーネントのキーと同じにすることはできません。
  • 静的 -レンダリング間でキーを変更しないでください。


key小道具の使用

上記の説明に従って、以下のサンプルを注意深く検討し、可能な場合は、推奨されるアプローチを実装してください。


悪い(潜在的に)

<tbody>
    {rows.map((row, i) => {
        return <ObjectRow key={i} />;
    })}
</tbody>

これは間違いなく、Reactで配列を反復処理するときに見られる最も一般的な間違いです。このアプローチは技術的に「間違っている」わけではなく、単に... あなたが何をしているのかわからない場合は「危険」です。静的配列を反復処理する場合、これは完全に有効なアプローチです(たとえば、ナビゲーションメニューのリンクの配列)。ただし、アイテムを追加、削除、並べ替え、またはフィルタリングする場合は、注意が必要です。公式ドキュメントでこの詳細な説明をご覧ください。

このスニペットでは、非静的配列を使用しており、スタックとしての使用に限定していません。これは危険なアプローチです(理由は後で説明します)。配列の先頭にアイテムを追加すると(基本的にはシフトなし)、それぞれの値は<input>そのまま残ります。どうして?keyは各アイテムを一意に識別しないためです。

つまり、最初Item 1はを持っていkey={0}ます。2番目のアイテムを追加すると、一番上のアイテムがになりItem 2Item 12番目のアイテムが続きます。しかし、今でItem 1はもうkey={1}ありませkey={0}ん。代わりに、Item 2今持っていますkey={0}!!

そのため、Reactは<input>Itemwithキー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


1
公式ドキュメント、彼らが使うtoString()代わりに、数として残しの文字列に変換します。これは覚えておくことが重要ですか?
skube 2017

1
@skube、いいえ、整数も使用できますkey。彼らがそれを変換している理由がわからない。
Chris

1
あなた整数使うことができると思いますが、あなたすべきですか?彼らのドキュメントによると、彼らは「...キーを選択する最良の方法は、一意に識別する文字列を使用することです...」(強調は私のものです)
skube

2
@skube、はい、それは完全に許容できます。上記の例で述べたように、反復配列の項目のインデックスを使用できます(これは整数です)。ドキュメントでも、「最後の手段として、配列内の項目のインデックスをキーとして渡すことができます」と記載されています。何が起こるかはいえということでkey、常にとにかく文字列をされて終わります。
Chris

3
@ farmcommand2、キーはReactコンポーネントに適用され、兄弟間で一意である必要があります。これは上で述べられています。つまり、配列内で一意
Chris

8

これは誰かを助けるかどうかはわかりませんが、クイックリファレンスになる場合があります。これは、上記のすべての回答と同様です。

以下の構造を使用してリストを生成する場所がたくさんあります。

return (
    {myList.map(item => (
       <>
          <div class="some class"> 
             {item.someProperty} 
              ....
          </div>
       </>
     )}
 )

少しの試行錯誤(およびいくつかの不満)の後、最も外側のブロックにキープロパティを追加することで解決しました。また、<>タグがタグに置き換えられていることにも注意してください。

return (

    {myList.map((item, index) => (
       <div key={index}>
          <div class="some class"> 
             {item.someProperty} 
              ....
          </div>
       </div>
     )}
 )

もちろん、上記の例では、単純に反復インデックス(index)を使用してキー値を設定しています。理想的には、リストアイテムに固有の何かを使用します。


これは非常に役に立ちました。ありがとうございました。私はそれを最外層に入れなければならないことにさえ気がつきませんでした
Charles Smith

6

警告:配列または反復子の各子には、一意の「キー」プロップが必要です。

これは、繰り返し処理する配列項目については一意の類似性が必要になるため、警告です。

Reactは、反復するコンポーネントレンダリングを配列として処理します。

これを解決するより良い方法は、反復する配列項目にインデックスを提供することです。次に例を示します。

class UsersState extends Component
    {
        state = {
            users: [
                {name:"shashank", age:20},
                {name:"vardan", age:30},
                {name:"somya", age:40}
            ]
        }
    render()
        {
            return(
                    <div>
                        {
                            this.state.users.map((user, index)=>{
                                return <UserState key={index} age={user.age}>{user.name}</UserState>
                            })
                        }
                    </div>
                )
        }

インデックスは、React組み込みの小道具です。


2
アイテムがどういうわけか再配置される場合、このアプローチは潜在的に危険です。しかし、それらが静的なままであれば、これは問題ありません。
クリス

@chrisこの場合、インデックスが重複する可能性があるため、私はあなたに完全に同意します。キーには動的な値を使用することをお勧めします。
Shashank Malviya

@chris私もあなたのコメントに同意します。重複する可能性があるため、インデックスではなく動的な値を使用する必要があります。簡単にするためにこれを行いました。貢献してくれてありがとう(賛成)
Malviya

6

コンポーネントに一意のキーを追加するだけです

data.map((marker)=>{
    return(
        <YourComponents 
            key={data.id}     // <----- unique key
        />
    );
})

6

チェック:キー= undef !!!

警告メッセージも受け取りました:

Each child in a list should have a unique "key" prop.

あなたのコードが完全で正しい場合、しかしオンの場合

<ObjectRow key={someValue} />

someValueは未定義です!!! まずこれを確認してください。時間を節約できます。


1

反応で一意のキーを定義する最適なソリューション:マップ内で名前postを初期化してからkey = {post.id}でキーを定義するか、私のコードで名前項目を定義してからkey = {item.idでキーを定義します}:

<div className="container">
                {posts.map(item =>(

                    <div className="card border-primary mb-3" key={item.id}>
                        <div className="card-header">{item.name}</div>
                    <div className="card-body" >
                <h4 className="card-title">{item.username}</h4>
                <p className="card-text">{item.email}</p>
                    </div>
                  </div>
                ))}
            </div>


0

これは警告ですが、これに対処すると、Reactsのレンダリングがはるかに高速になります

これはReact、リスト内の各アイテムを一意に識別する必要があるためです。そのリストの要素の状態がReactsで変更されたVirtual DOM場合、Reactはどの要素が変更されたか、DOMのどこで変更する必要があるかを把握して、ブラウザーのDOMがReacts仮想DOMと同期するようにする必要があります。

解決策として、keyliタグに属性を導入するだけです。これkeyは、各要素に固有の値である必要があります。


これは完全に正しいわけではありません。小道具を追加すると、レンダリングが速くなりませんkey。指定しない場合、Reactが自動的に割り当てます(現在の反復のインデックス)。
Chris

その場合@Chrisはなぜ警告を出しますか?
プライム

キーを提供しないと、Reactはデータがどのようにモデル化されているかを認識できません。これにより、配列が変更された場合に、望ましくない結果が生じる可能性があります。
Chris

@Chrisは、配列を変更する場合、キーを指定しなかった場合、Reactがそれに応じてインデックスを修正します。とにかく、Reactから余分なオーバーヘッドを削除すると、レンダリングプロセスにある程度の影響が出ると思いました。
プライム

繰り返しになりますが、Reactは基本的にそうしますkey={i}。したがって、配列に含まれるデータによって異なります。たとえば、リストがある場合["Volvo", "Tesla"]、Volvoは明らかにキーとテスラで識別されます。これは、ループに現れる順序だからです。配列を並べ替えると、キーが入れ替わります。Reactの場合、「オブジェクト」はまだ一番上にあるので、この変更は、並べ替えではなく「名前の変更」として解釈されます。ここでの正しいキーは、次に、である必要があります。常にランタイムの途中で並べ替えるわけではありませんが、並べ替えるとリスクが生じます。01010
Chris

0
var TableRowItem = React.createClass({
  render: function() {

    var td = function() {
        return this.props.columns.map(function(c, i) {
          return <td key={i}>{this.props.data[c]}</td>;
        }, this);
      }.bind(this);

    return (
      <tr>{ td(this.props.item) }</tr>
    )
  }
});

これは問題を解決します。


0

<></>代わりnullに返される必要があるときに配列内の一部のアイテムが返されるため、このエラーメッセージが表示されていました。


-1

このようにキーごとにGuidを使用してこれを修正しました:Guidの生成:

guid() {
    return this.s4() + this.s4() + '-' + this.s4() + '-' + this.s4() + '-' +
        this.s4() + '-' + this.s4() + this.s4() + this.s4();
}

s4() {
    return Math.floor((1 + Math.random()) * 0x10000)
        .toString(16)
        .substring(1);
}

そして、この値をマーカーに割り当てます:

{this.state.markers.map(marker => (
              <MapView.Marker
                  key={this.guid()}
                  coordinate={marker.coordinates}
                  title={marker.title}
              />
          ))}

2
キーとして乱数を追加することは有害です!キーの目的である変更を検出するための反応を防ぎます。
pihentagy

-1

基本的に、配列のインデックスを一意のキーとして使用しないでください。代わりに、を使用しsetTimeout(() => Date.now(),0)て、一意のキーまたは一意のuuid、または一意のIDとして配列のインデックスではなく一意のIDを生成するその他の方法を設定できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.