React では複数要素を返すときにラッパーが必要になる
React でコンポーネントを作っていると、1つのコンポーネントは必ず「単一の要素」を返す必要がある、というルールに必ずぶつかります。
例えばこんなコードは「返すのは1つの要素にしてください」と怒られてしまいます。
// NG例:return 内に複数要素
return (
<h1>Hello</h1>
<p>World</p>
);
ありがちな <div> 乱立問題
実際のプロジェクトでは、この制約を回避するためにとりあえず <div> でラップするパターンをよく見かけます。
return (
<div>
<h1>Hello</h1>
<p>World</p>
</div>
);
一見問題なさそうに見えますし動くのですが、プロジェクトが大きくなると <div> だらけになり、「div地獄」 に陥ります。
特に UI ライブラリや CSS でスタイリングしているとき、不要な <div> が DOM に増えていくと、
- 余計なスペースや余白が発生する
- CSS セレクタが複雑になる
- テーブルやリスト構造で HTML の仕様に違反する(今回のような
<tr>が<div>の下に来るエラー)
といった問題が出てきます。
実際に テーブル行をループで生成する場面で <div> を入れてしまい、validateDOMNesting の警告が出た経験があります。
React.Fragmentとは
公式ドキュメントによるとReact.Fragment は「DOM に余分なノードを追加せずに子要素をグループ化できるコンポーネント」です。
つまり、複数要素をまとめたいけど <div> など余計なタグを増やしたくないときに使います。
イメージ的には「見えない箱」みたいな存在です。
<React.Fragment> と省略記法 <>...</> の違い
React では Fragment を書く方法が2つあります。
- フル表記:
return (
<React.Fragment>
<h1>Hello</h1>
<p>World</p>
</React.Fragment>
);
- 省略記法(ショートハンド):
return (
<>
<h1>Hello</h1>
<p>World</p>
</>
);
どちらも同じ意味ですが、違いは key を使えるかどうかです。
-
<React.Fragment key={...}>→ OK -
<>{...}</>→keyをつけられない
そのため、リストレンダリング時など key が必要な場面ではフル表記を使うのがベストです。
DOM に出力されないことを確認できるサンプル
実際に Fragment を使ったときの DOM 出力を見てます。
function Sample() {
return (
<>
<h1>Hello</h1>
<p>World</p>
</>
);
}
このコンポーネントをレンダリングすると、ブラウザの DevTools には次のように表示されます。
<h1>Hello</h1>
<p>World</p>
余計な <div> や <span> は一切出ていません。
もしこれを <div> でラップしていたら、こうなります。
<div>
<h1>Hello</h1>
<p>World</p>
</div>
わずかな違いですが、これが積み重なると DOM が肥大化したり、CSS やレイアウトに悪影響を与える原因になります。
よくある使いどころ
テーブル構造での validateDOMNesting エラー回避
HTML の仕様では <tbody> 直下には <tr> しか書けません。
プロジェクトでも、ループ処理の中で <div> を挟んでしまい、以下のような警告が出ました。
Warning: validateDOMNesting(...): <tr> cannot appear as a child of <div>.
修正前(エラーになる例):
<Table.Tbody>
<div> {/* tbody直下にdivはNG */}
<Table.Tr>...</Table.Tr>
</div>
</Table.Tbody>
修正後(Fragmentで解決):
<Table.Tbody>
<>
<Table.Tr>...</Table.Tr>
</>
</Table.Tbody>
DOMに余計なタグを挟まないので、テーブル構造を壊さずに済むのが大きな利点です。
コンポーネントの return で兄弟要素を返したいとき
特に小さな UI 部品を作るとき、return 内で複数要素を返したくなることがあります。
Fragment があればラップ用のタグを置かずに実現できます。
function LabelAndInput() {
return (
<>
<label>名前</label>
<input type="text" />
</>
);
}
応用パターン
key が必要な場面での利用
map で複数要素を返すとき、key を指定しないと React が警告を出します。
このときショートハンド <>...</> では key をつけられないため、フル表記の<React.Fragment> を使う必要があります。
// NG:ショートハンドではkeyを使えない
list.map((item) => (
<>
<li>{item.title}</li>
<li>{item.value}</li>
</>
));
// OK:React.Fragmentならkeyが使える
list.map((item) => (
<React.Fragment key={item.id}>
<li>{item.title}</li>
<li>{item.value}</li>
</React.Fragment>
));
条件分岐の返却をシンプルにする
三項演算子や条件分岐で複数要素を返したいときにも便利です。
{isLoggedIn ? (
<>
<p>ログイン中</p>
<button>ログアウト</button>
</>
) : (
<>
<p>ゲスト</p>
<button>ログイン</button>
</>
)}
ライブラリとの相性
UI ライブラリ(Mantine, Material UI など)は内部で厳格な DOM 構造を要求することがあります。
特に <table> / <ul> / <select> 系で <div> を挟むとエラーになるケースは多いので、
「とりあえずFragmentでくくる」が鉄則になります。
注意点
ショートハンドでは key が使えない
先ほども触れましたが、省略記法 <>...</> では key をつけられません。
リストレンダリングなど key が必須な場面では必ず <React.Fragment key={...}> を使うようにしましょう。
props は渡せない
Fragment 自体は 純粋にラッパー用途なので、className や style などの属性を渡すことはできません。
もしスタイリングが必要なら、素直に <div> や <span> を使うのが正解です。
過剰に使わない
あるあるですが、便利だからといって過剰に使うと逆にコードが読みにくくなります。
「DOMに不要なタグを出したくない」ときにだけ使うのがベストです。
まとめ
-
React.Fragment は「見えないラッパー」
DOM に余計なノードを増やさずに複数要素をまとめられる。-
div地獄を回避してDOMをクリーンに保つ - テーブル構造などで
validateDOMNestingエラーを防ぐ - コンポーネントやリストで複数要素を返したいとき
-
keyが必要な場合は<React.Fragment key={...}> - 条件分岐やUIライブラリとの組み合わせでDOM構造を壊さない
-