【実務・中級編】 React.ReactNode型の役割 – React実践ガイド

React.ReactNodeの正体:なぜ「とりあえずこれ」と指定するのか、その深淵を覗く

現場でコードをレビューしていると、コンポーネントのPropsに `children: React.ReactNode` を指定しているコードによく出会います。初心者を卒業したエンジニアの多くが「とりあえず `ReactNode` にしておけばエラーが出ないから」という理由でこれを使っています。

しかし、なぜ `ReactElement` や `JSX.Element` ではなく、あえて `ReactNode` なのか。その境界線を曖昧にしたまま開発を続けるのは、例えるなら「中身が何か分からない箱を、中身を確認せずに運び続ける」ようなものです。

今日は、Reactの型定義におけるこの「究極の汎用型」の正体と、現場で後悔しない使い分けの極意を伝授します。

—

React.ReactNode が許容する「広すぎる世界」

まず、結論から言います。`ReactNode` は、Reactがレンダリングできるあらゆるものを内包するユニオン型です。

type ReactNode =
| ReactElement // JSXタグそのもの

| string // テキスト
| number // 数値
| ReactFragment // <>
| ReactPortal // createPortalで生成されたもの
| boolean // true/false(何もレンダリングされないが型としては許容される)
| null // 何も表示しない
| undefined // 何も表示しない
| ReactNodeArray // 上記の配列

これを見て分かる通り、`ReactNode` は「画面に表示されうる最小単位から、複雑なコンポーネントツリーまで」を飲み込むブラックホールのような型です。

ブラウザの裏側で何が起きているか

Reactのレンダリングプロセスにおいて、`ReactNode` は最終的にFiberツリーというReact独自の内部構造へ変換されます。Reactは、この「何でもあり」の型を再帰的に走査し、テキストノードなのか、DOMノードなのかを判定して、DOM API(`createElement` や `textContent` の更新)を叩きます。

もし型定義を `string` に限定してしまうと、親コンポーネントがアイコンを渡した瞬間にTypeScriptの静的解析で弾かれます。「柔軟なコンポーネント設計」を目指すなら、この懐の深さこそが武器になります。

—

実務で「ReactNode」を使うべき場面と、避けるべき場面

現場での設計思想として、以下の基準を設けてみてください。

1. 基本は `ReactNode` で良い(レイアウトコンポーネント)
`Container` や `Card` のような、中身に何が入るか依存しないコンポーネントの場合、`ReactNode` は最適解です。
2. 「厳密さ」が必要な場合は型を絞る
例えば「特定のコンポーネント(例: `MenuItem`)しか受け取りたくない」という場合、`ReactNode` は緩すぎます。その場合は `ReactElement` を使いましょう。

実践的なサンプルコード

以下のコードは、現場ですぐに使える汎用的なレイアウトコンポーネントの例です。

import React from ‘react’;

type LayoutProps = {
// コンポーネントを囲むための標準的な定義
children: React.ReactNode;
// タイトルは文字列のみに限定したい場合
title: string;
};

export const Card = ({ children, title }: LayoutProps) => {
return (

);
};

—

「ReactElement」との決定的な違い

たまに `children: React.JSX.Element` と書く人がいますが、これはおすすめしません。

  • `ReactElement`: オブジェクト形式のJSX(`{ type: ‘div’, props: {…} }`)のみ。つまり `string` や `number` を直接渡すことができません。
  • `ReactNode`: 上記の通り、`string` や `number` も含みます。

もし `ReactElement` を使ってしまうと、親コンポーネントで以下のような記述をした瞬間にTypeScriptが吠えます。

// ❌ children: ReactElement を期待している場合

こんにちは! {/ stringはReactElementではないためエラー! /}

現場のUI開発において、テキストを直接コンポーネントに渡すことは頻繁に発生します。「文字列も一つのノードである」というReactの哲学を理解していれば、`ReactNode` がなぜ標準なのか、納得できるはずです。

—

シニアからの最後のアドバイス

Reactにおける型定義は、「何を許容し、何を禁止するか」という設計意図そのものです。

  • 迷ったら `ReactNode`:柔軟性を最大化できます。
  • 意図があるなら絞る:特定のUIパターンを強制したいなら `ReactElement` や `ReactNode[]` を検討してください。

「型はドキュメントである」とよく言われます。`children: React.ReactNode` と書くことは、「このコンポーネントは、Reactが扱えるどんな表現でも歓迎しますよ」という、開発者に対する意思表示なのです。

この「広さ」の正体を理解した上で、自信を持って型を使い分けてください。あなたの書くコードが、他のチームメンバーにとって読みやすく、そして壊れにくいものになることを願っています。

コメント

タイトルとURLをコピーしました