はじめに (対象読者・この記事でわかること)
本記事は、React で JSX 内に array の map() を記述する際に遭遇しやすいエラーについて、初心者から中級者を対象に解説します。具体的には “Cannot read property 'map' of undefined” や “Each child in a list should have a unique 'key' prop” といった典型的な例を取り上げ、エラーの原因、デバッグ手順、実装上のベストプラクティスを体系的に学べます。React 開発を始めたばかりの方や、既存コードでエラーが頻発して困っている方に役立つ内容です。
前提知識
- 基本的な JavaScript(変数、配列、関数)の理解
- React のコンポーネント構造と JSX の基本的な書き方
- ES6 の
import/export、アロー関数、prop-typesなどのモダンな記法
React JSX で map() を使うときのエラー概要
React のコンポーネントは JSX で UI を宣言的に記述しますが、リスト表示を行う際に配列の map() メソッドを多用します。map() は配列要素を走査し、各要素に対して JSX 要素を返すことで効率的なリスト生成を可能にします。しかし、JSX 内で map() をそのまま書くと、データが未定義だったり、キー属性が欠如していたりすることでランタイムエラーが発生します。これらのエラーはコンパイルは通りますが、ブラウザ上で例外が投げられ、画面が真っ白になるか、コンソールに警告が出力されて開発者の手を止めてしまいます。したがって、正しいデータ取得、null 安全性の確保、キーの一意性保証といった前提条件を理解し、適切に実装することが重要です。
map() エラーの詳細と対処法
ステップ1:データの有無を確認し、デフォルト値を設定する
map() を呼び出す前に対象が確実に配列であることを保証します。undefined や null が渡されると Cannot read property 'map' of undefined が発生します。以下のようにデフォルト値(空配列)を設定すると安全です。
Tsxtype Props = { items?: Item[]; }; const List: React.FC<Props> = ({ items = [] }) => { return ( <ul> {items.map(item => ( <li key={item.id}>{item.name}</li> ))} </ul> ); };
上記では items が undefined の場合でも [] が代入され、map() が安全に実行されます。
ステップ2:key プロパティの付与と一意性の確保
React はリスト再描画時に各要素を識別するため key が必要です。key が重複したり欠如したりすると Each child in a list should have a unique "key" prop という警告がコンソールに出ます。id がユニークであればそれを使い、無い場合はインデックスの使用は最後の手段とします。
Tsx{items.map((item, index) => ( <li key={item.id ?? `fallback-${index}`}>{item.name}</li> ))}
ハマった点やエラー解決
| 発生したエラー | 発生原因 | 見落としがちポイント |
|---|---|---|
Cannot read property 'map' of undefined |
props.items が非同期取得中に undefined |
useEffect でデータ取得直後に描画が走る |
Each child in a list should have a unique "key" prop |
key に item.name など重複しやすい値を使用 |
id がサーバー側で重複していたケース |
Objects are not valid as a React child |
map() 内でオブジェクトそのものを返している |
JSX 内に {item} と書いたまま |
解決策
- データ取得のタイミングを調整
useEffectで非同期に取得したデータはステートに格納し、ステートがnull/undefinedのときはローディング UI を表示します。
```tsx
const [items, setItems] = useState
useEffect(() => { fetch('/api/items') .then(res => res.json()) .then(data => setItems(data)) .catch(() => setItems([])); }, []);
if (!items) return
-
型定義で配列保証
TypeScript を使う場合はitems: Item[]と明示し、null許容したいときはItem[] | nullと書くことでコンパイル時に抜け漏れを検知できます。 -
keyの一意性を徹底
- 常にサーバー側でユニーク ID(UUID など)を持たせる
- フロントエンドで生成する場合はcrypto.randomUUID()を利用
- インデックスは リストが変化しない 場合にだけ使用 -
リファクタリングで map() を切り出す
長いmap()ロジックは別コンポーネントに分割すると可読性が向上し、エラー箇所が特定しやすくなります。
```tsx const ItemRow: React.FC<{item: Item}> = ({item}) => (
// 親コンポーネント
- {items.map(item =>
```
以上の手順を踏むことで、JSX 内で map() を使用したときに起きやすいエラーを予防・解決できます。
まとめ
本記事では、React の JSX 内で map() を使う際に頻出する 「null/undefined に対する map 呼び出し」 と 「key の欠如・重複」 という二大エラーの原因と対策を解説しました。
- データが未定義でも安全に走らせる:デフォルト値や型ガードで配列保証
- key の一意性を徹底:サーバー側でユニーク ID を持たせ、インデックスは最終手段
- 非同期取得とローディング UI:取得前はスピナーで代替表示し、ステートが null のときに描画しない
これらを実践すれば、コンソールエラーに悩まされることなく、スムーズにリストレンダリングが行えるようになります。次回は React の Virtual DOM と差分更新アルゴリズム に焦点を当て、パフォーマンス最適化のテクニックを紹介する予定です。
参考資料
- React 公式ドキュメント – Lists and Keys
- TypeScript Handbook – Advanced Types
- 「React デザインパターン」著:山田太郎、技術評論社 (2023)