✅ この記事の目的
-
map()を用いたリスト描画の方法 - 一意で安定した key を付与する理由(index を使わない)
- リストが複雑化した際に子コンポーネントへ分割する判断基準と実践手順
📂 目次
- リスト描画の基本:
map()と key の役割 - 正しい key の条件:一意性・安定性・再現性
- リストのコンポーネント分割
- まとめ
- 関連リンク
1. リスト描画の基本:map() と key の役割
React のリスト描画は Array.prototype.map() を使って要素をコンポーネントへ展開します。この際、どの要素がどのコンポーネントに対応するかを React が正しく識別するために key プロパティが必要となります。
type User = { id: string; name: string; email: string };
export function UserList({ users }: { users: User[] }) {
return (
<ul>
{users.map((user) => (
<li key={user.id}>
<span>{user.name}</span> — <span>{user.email}</span>
</li>
))}
</ul>
);
}
- コンポーネントの識別と更新:配列の要素とコンポーネントの対応を React が判断するために必要。これにより、移動・挿入・削除が発生しても DOM を正しく更新できる。
- 兄弟要素間での一意性:同じ map 内で一意であればよい(グローバルな一意性は不要)。
2. 正しい key の条件:一意性・安定性・再現性
- 一意性:同一リスト内(兄弟要素間)で重複しない。
- 安定性:位置が変わっても同じデータに同じ key を割り当てられる。
- 再現性:再レンダー後も同じルールで同じ key が導出できる。
良い例
- DB 主キー(user.idなど) ← 推奨
- 不変な一意識別子(データ作成時に crypto.randomUUID() 等で付与された固定 ID)
- 合成キー(orderId-lineItemIdのように複数属性から導出)
- 自然キー(外部システムの安定 ID/変化しづらいメールアドレスなど)
避ける例
- Math.random()や毎レンダー生成される UUID(再現性がなく、毎回リセットされる)
- 配列 index(並べ替え・挿入・削除で位置が変わり、状態がズレる)
3. リストのコンポーネント分割
リストの 1 要素(<li> など)が複雑になった場合、可読性と再利用性のために子コンポーネント(例:UserItem)へ分割します。
分割の目安
- 1行が複数の UI 要素・イベント・ローカル state・副作用を持つ
- 各要素が独自の State(「編集モード」など)を持つようになった。
- 1つのコンポーネントに複数の map() があり、構造が追いづらい。
- 30〜50 行を超えて見通しが悪い
- 行単位のメモ化(
React.memo)で再レンダー抑制したい
key 設定の鉄則:map の直下に書く
コンポーネントを分割しても、key は map() メソッドのすぐ内側の要素に記述しなければなりません。子コンポーネント側に key を移すと、React は正しく識別できなくなります。
// ✅ 正しい例
{
users.map((user) => (
<UserItem key={user.id} user={user} /> // 呼び出し側に key を書く
));
}
// ❌ 悪い例
{
users.map((user) => <UserItem user={user} />);
}
// UserItem の中で key を指定しても React はそれを無視する
function UserItem({ user }) {
return <li key={user.id}>{user.name}</li>;
}
⚠️ 注意:Fragment を使う場合
map() の中で複数の要素を返したい場合、短縮記法の <> ~ </> では key を指定できません。その際は React.Fragment を明示的に使用してください。
{
items.map((item) => (
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</React.Fragment>
));
}
✅ 4. まとめ
- リスト描画は
map()、key は「一意・安定・再現可能」な識別子を使う -
index を key にするのは原則禁止:状態ズレ/UX 劣化/バグの温床
ただし、並び替えや増減が「絶対に発生しない」静的な表示に限り許容される - 複雑な行は 子コンポーネントへ分割し、
React.memoと組み合わせて保守性・性能を向上 - 仕様変更時は key の安定性が破壊されていないか必ず見直す
- Fragment を使う際は key を指定できる 形式で記述する
📚 5. 関連リンク
- React 公式ドキュメント:Lists and Keys
https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key - React 公式ドキュメント:Rendering Lists(旧ドキュメント)
https://legacy.reactjs.org/docs/lists-and-keys.html - React 公式ドキュメント:Memoizing(
React.memo)
https://react.dev/reference/react/memo