Reactでリストをレンダリングする際の落とし穴!"Do not use Array index in keys"エラーの解決策
ReactJS での "error: Do not use Array index in keys" エラーとその解決策
このエラーは、ReactJS でリストや配列などの要素をレンダリングする際に、要素の key
プロパティにインデックスを使用している場合に発生します。
問題点
インデックスを key
プロパティとして使用すると、以下の問題が発生します。
リストの要素が変更された場合、React がどの要素が変更されたのかを正しく認識できなくなる
- 例えば、リストの要素を並べ替えたり、削除したりすると、React がどの要素が移動または削除されたのかを誤って認識し、意図しないレンダリングが行われる可能性があります。
パフォーマンスが低下する
解決策
インデックスを key
プロパティとして使用する代わりに、以下のいずれかの方法で要素にユニークなキーを割り当てる必要があります。
const items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' },
];
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
- 要素のインデックスと他のプロパティを組み合わせて
key
プロパティを作成する- 例えば、要素のインデックスと名前を組み合わせて
key
プロパティを作成できます。
- 例えば、要素のインデックスと名前を組み合わせて
const items = [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' },
];
<ul>
{items.map((item, index) => (
<li key={`${index}-${item.name}`}>{item.name}</li>
))}
</ul>
uuid
ライブラリなどのライブラリを使用して、ランダムなユニークな ID を生成する- 例えば、
uuid
ライブラリを使用して、各要素にランダムなユニークな ID を生成できます。
- 例えば、
import { v4 as uuid } from 'uuid';
const items = [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' },
];
<ul>
{items.map((item) => (
<li key={uuid()}>{item.name}</li>
))}
</ul>
eslint-plugin-react を使用している場合は、以下の設定を追加することで、このエラーを警告またはエラーとして検出することができます。
{
"plugins": [
"react"
],
"rules": {
"react/no-array-index-key": "error"
}
}
- このエラーは、React 16.8 以降で導入された
react/no-array-index-key
ルールによって検出されます。 - このルールは、デフォルトでは警告として設定されていますが、エラーとして設定することもできます。
import React from 'react';
const items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' },
];
const App = () => {
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
};
export default App;
このコードを実行すると、以下の HTML が出力されます。
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
このコードは、以下の点に注意して書かれています。
key
プロパティには、要素の ID を使用しています。map
関数をを使用して、リストの要素をループしています。- 各要素に対して、
li
タグをレンダリングしています。
以下のコードは、要素のインデックスと名前を組み合わせて key
プロパティを作成する方法を示しています。
import React from 'react';
const items = [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' },
];
const App = () => {
return (
<ul>
{items.map((item, index) => (
<li key={`${index}-${item.name}`}>{item.name}</li>
))}
</ul>
);
};
export default App;
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
key
プロパティには、要素のインデックスと名前を組み合わせて作成しています。
npm パッケージ
以下のコードは、uuid
パッケージを使用して、ランダムなユニークな ID を生成する方法を示しています。
import React from 'react';
import uuid from 'uuid';
const items = [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' },
];
const App = () => {
return (
<ul>
{items.map((item) => (
<li key={uuid()}>{item.name}</li>
))}
</ul>
);
};
export default App;
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
const items = [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' },
];
<ul>
{items.map((item, index) => (
<li key={index}>{item.name}</li>
))}
</ul>
この方法は、インデックスを直接 key
プロパティとして使用することは避けますが、依然としてインデックスに依存しているため、リストの要素が変更された場合に問題が発生する可能性があります。
forEach 関数を使用する
const items = [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' },
];
<ul>
{items.forEach((item, index) => (
<li key={`${index}-${item.name}`}>{item.name}</li>
))}
</ul>
この方法は、map
関数よりも簡潔ですが、map
関数で利用できるすべての機能は利用できません。
カスタムフックを使用する
import React, { useState } from 'react';
const useUniqueKey = () => {
const [count, setCount] = useState(0);
return () => {
setCount((prevCount) => prevCount + 1);
return count;
};
};
const items = [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' },
];
const App = () => {
const uniqueKey = useUniqueKey();
return (
<ul>
{items.map((item) => (
<li key={uniqueKey()}>{item.name}</li>
))}
</ul>
);
};
export default App;
この方法は、より柔軟性と再利用性を高めることができますが、コードが複雑になる可能性があります。
React.useMemo フックを使用する
import React, { useState, useMemo } from 'react';
const items = [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' },
];
const App = () => {
const [itemsData, setItemsData] = useState(items);
const keyGenerator = useMemo(() => {
const keys = {};
return (item) => {
if (!keys[item.name]) {
keys[item.name] = 0;
}
keys[item.name]++;
return `${item.name}-${keys[item.name]}`;
};
}, []);
return (
<ul>
{itemsData.map((item) => (
<li key={keyGenerator(item)}>{item.name}</li>
))}
</ul>
);
};
export default App;
この方法は、useUniqueKey
フックよりも効率的ですが、コードが複雑になる可能性があります。
サードパーティのライブラリを使用する
import React from 'react';
import { useMemo } from 'react-use';
import { v4 as uuid } from 'uuid';
const items = [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' },
];
const App = () => {
const keyGenerator = useMemo(() => uuid, []);
return (
<ul>
{items.map((item) => (
<li key={keyGenerator()}>{item.name}</li>
))}
</ul>
);
};
export default App;
この方法は、サードパーティのライブラリに依存するため、コードが煩雑になる可能性があります。
reactjs npm