1. ホーム
  2. React

【React】Fragment(<>)の使い方|余分なdivを増やさず複数要素を返す方法

Share

React でコンポーネントを書いていると、「複数の要素を返したいのにエラーになる」「とりあえず <div> で囲んで解決したけれど、余計な div が積み重なってしまう」という悩みに出会います。React のコンポーネントは要素を1つだけ返すという決まりがあるため、並んだ複数の要素はまとめる必要があります。このときに、余分な HTML を一切増やさずにまとめられるのが Fragment(フラグメント)です。この記事では、Fragment がなぜ必要なのか、<></> の省略記法と <React.Fragment> の使い分け、リスト表示で key を付けたいときの書き方、そして余分な div がレイアウトを崩す問題までを、初心者向けに解説します。

なぜ複数要素をそのまま返せないのか

まず、Fragment が必要になる理由を確認しましょう。次のように return の中で2つの要素を並べて返そうとすると、エラーになります。

error-example.tsx
function Profile() {
  // エラー: 隣り合う複数の要素は1つにまとめる必要がある
  return (
    <h1>佐藤さん</h1>
    <p>フロントエンドエンジニア</p>
  );
}

JSX(JavaScript の中に HTML のような記述を書ける構文)は、内部的には1回の return で1つの値を返す関数呼び出しに変換されます。そのため、並んだ2つの要素をそのまま返すことはできません。もっとも手軽な解決策は <div> で囲むことですが、意味を持たない div がページ中に増えていくと、後述するようにレイアウトの妨げになることがあります。そこで Fragment の出番です。

Fragment で要素をまとめる

Fragment は、実際の HTML を出力せずに複数の要素をまとめるための特別なラッパーです。<React.Fragment> と書く方法と、それを短くした <></>(空タグ)という記法があります。普段は短い記法で十分です。

Profile.tsx
// 空タグ <> でまとめる(推奨の書き方)
function Profile() {
  return (
    <>
      <h1>佐藤さん</h1>
      <p>フロントエンドエンジニア</p>
    </>
  );
}

<></> で囲むだけで、エラーは解消します。このとき DOM(実際に描画される HTML)には <h1><p> だけが並び、それらを包む余計な要素は一切出力されませんdiv で囲んだ場合との違いを、次の表で整理します。

まとめ方DOM に出力される要素
<div>...</div>div が1つ増える(中身を包む)
<>...</>(Fragment)何も増えない(中身だけが並ぶ)

短縮記法と React.Fragment の使い分け

<></> の短縮記法はとても便利ですが、1つだけできないことがあります。それは key 属性を付けることです。後述するリスト表示で key が必要な場面では、短縮記法ではなく <React.Fragment>(インポートすれば <Fragment>)と正式な形で書く必要があります。両者の違いはこれだけで、DOM に何も出力しない点は同じです。

two-forms.tsx
import { Fragment } from 'react';

// 短縮記法: key は付けられない
const a = (
  <>
    <span>A</span>
    <span>B</span>
  </>
);

// 正式な記法: key などの属性を付けられる
const b = (
  <Fragment key="group1">
    <span>A</span>
    <span>B</span>
  </Fragment>
);

ふだんは短縮記法を使い、key が必要なときだけ <Fragment> を使う、という使い分けで問題ありません。

リスト表示で key を付けたいとき

map でリストを作るとき、1件につき複数の要素(たとえば <dt><dd>)を出力したいことがあります。React ではリストの各項目に key(各要素を見分けるための目印)が必要ですが、余計な div で包みたくありません。こういうときこそ、key を付けられる <Fragment> が役立ちます。

Glossary.tsx
import { Fragment } from 'react';

type Term = { id: number; word: string; desc: string };

function Glossary({ terms }: { terms: Term[] }) {
  return (
    <dl>
      {terms.map((t) => (
        // 1件ごとに dt と dd をまとめ、key は Fragment に付ける
        <Fragment key={t.id}>
          <dt>{t.word}</dt>
          <dd>{t.desc}</dd>
        </Fragment>
      ))}
    </dl>
  );
}

<dl> の直下には <dt><dd> だけを置くのが正しい構造です。もしここで div を挟むと、定義リストの構造が崩れてしまいます。<Fragment key={t.id}> なら、余計な要素を出さずに key も付けられるため、この形にぴったり合います。

余分な div がレイアウトを崩すとき

テーブルの構造が壊れる

<table> の中では、<tr> の直下に <td> を並べるという構造が決まっています。ここでセルのまとまりを div で包むと、テーブルとして正しくない HTML になり、表示が崩れます。複数の <td> をまとめて返したいコンポーネントでは、div ではなく Fragment を使うことで構造を保てます。

Flexbox や Grid の直下の子が増える

親要素に display: flexdisplay: grid を指定していると、その直下の子要素が並びの対象になります。ここで意味のない div を1枚挟むと、本来横並びにしたかった要素がその div の中に入ってしまい、レイアウトが意図通りになりません。Fragment なら余計な階層を作らないので、Flexbox や Grid の子要素の並びをそのまま保てます。こうした「ラップ用の要素が邪魔になる」場面こそ、Fragment がもっとも役立つところです。

まとめ

React のコンポーネントは要素を1つしか返せないため、複数の要素は何かでまとめる必要があります。div で囲む方法は手軽ですが、余分な要素が増え、テーブルや Flexbox・Grid のレイアウトを崩すことがあります。Fragment を使えば、DOM に何も出力せずに要素をまとめられます。ふだんは短縮記法 <></> を使い、map でのリスト表示のように key が必要なときだけ <Fragment key={...}> を使う、という使い分けを覚えておきましょう。「まとめたいけれど余計な HTML は増やしたくない」ときの定番として、Fragment はとても役立ちます。

参考ページ