0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

map()を用いたリスト描画の方法

0
Posted at

✅ この記事の目的

  • map() を用いたリスト描画の方法
  • 一意で安定した key を付与する理由(index を使わない)
  • リストが複雑化した際に子コンポーネントへ分割する判断基準と実践手順

📂 目次

  1. リスト描画の基本:map() と key の役割
  2. 正しい key の条件:一意性・安定性・再現性
  3. リストのコンポーネント分割
  4. まとめ
  5. 関連リンク

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. 関連リンク


0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?