【入門編】 React.ReactNodeとReact.ReactElementの厳密な使い分け – React実践ガイド

こんにちは!Reactの学習、楽しく進められていますか?
画面をパーツごとに分けて組み立てられるようになってくると、「おっ、なんだかすごいアプリを作っているぞ!」というワクワク感が出てきますよね。

でも、そんな楽しい時期に、多くの人がふと立ち止まって頭を抱えてしまう瞬間があります。それが「Props(プロパティ)の型定義」です。

特にTypeScriptと一緒にReactを書いていると、コンポーネントに「子ども(中身)」を渡したいとき、型として `React.ReactNode` を書くべきなのか、それとも `React.ReactElement` を書くべきなのか……。この二択で迷子になってしまう人が後を絶ちません。

ネットで調べても、なんだか小難しい英語のドキュメントや、機械的な定義ばかりが出てきて、「結局、普段はどっちを使えばいいのよ!」と画面の前で叫びたくなりますよね。

大丈夫です、安心してください。今日は、この2つの違いを「身近なお買い物の箱」にたとえて、誰でもスッキリ腑に落ちるように優しく解きほぐしていきますね。のんびりお茶でも飲みながら、一緒に見ていきましょう!

—

1. まずはイメージから!「ダンボール箱」と「中身の家具」

Reactでコンポーネントを作るとき、他のコンポーネントを包み込むような「枠組み(レイアウト用のコンポーネントなど)」を作ることがよくありますよね。例えば、「きれいなリボンがついたプレゼントボックス」や「頑丈なダンボール箱」を想像してみてください。

この箱の設計図(型定義)を書くとき、箱の中に何が入るのかをあらかじめ決めておく必要があります。ここで登場するのが、今日の主役である `ReactNode` と `ReactElement` です。

`ReactElement` = 「工場で作られた特定の家具(JSX要素)」

`ReactElement` は、JSX(HTMLタグのような見た目の書き方)で書かれた「厳密に形のあるもの」だけを指します。
例えるなら、「ニトリで買った組み立て済みの椅子」です。形がハッキリしていて、「これは椅子だ!」と誰が見ても分かります。ただ、「ただの文字列(ただの木材のかけら)」や「数字」は、この『椅子』のカテゴリには入りません。少しお高くとまった、厳格な会員制のクラブのようなイメージです。

`ReactNode` = 「箱に入れられるものなら、なんでもウェルカムな世界」

一方で、`ReactNode` は、もっと懐が深いです。
例えるなら、「引っ越しのダンボール箱になんでも詰めていいよ!」という状態です。

  • 椅子(ReactElement)も入る。
  • ただのメモ書き(文字列の `“こんにちは”`)も入る。
  • 数字の `123` も入る。
  • 「今回は中身なし!」(`null` や `undefined`)も許してくれる。

そう、`ReactNode` は、Reactの世界で画面に表示できるものなら、ほとんど何でも受け入れてくれる「究極の包容力」を持った型なんです。

—

2. 現場のプロはどう使い分けているの?

「じゃあ、全部 `ReactNode` にしておけば、エラーも出ないし万能なんじゃないの?」

鋭いですね!その通り、実は実務の現場でも、子要素(チルドレン)を受け取るコンポーネントの型には、ほとんどの場合 `ReactNode` が選ばれます。なぜなら、現場のコンポーネントは「文字だけじゃなく、時にはタグも、時にはただの数字も入れたい!」というワガママな要望に耐える必要があるからです。

では、一体どんなときに `ReactElement` の出番があるのでしょうか? それぞれの具体的な使い分けの基準をまとめてみました。

🌟 迷ったらこれ!圧倒的エースの `React.ReactNode`

  • いつ使う?: コンポーネントの中に、好きなようにHTMLタグやテキスト、他のコンポーネントを詰め込みたいとき(いわゆる `children` プロパティの型定義)。
  • なぜ?: 使う側が「何を中に入れるか」を自由に決められるようにするため。

🔒 厳しい番人!ここぞのときの `React.ReactElement`

  • いつ使う?: 「絶対にこのコンポーネントの中には、特定のJSX要素しか入れたくない!」という、厳密なルールを強制したいとき。例えば、タブ切り替えUIで「子要素として `` タグ以外は絶対に許さない!」という意地悪な(もとい、堅牢な)設計にしたい場合など。
  • なぜ?: 予期しない文字列やバグの混入を、TypeScriptのコンパイル段階でバシッと防ぎたいから。

—

3. 実例コードで触って感覚をつかもう

百聞は一見にしかず。実際にコードを見てみましょう。
エディタを開いたつもりで、以下のコードを眺めてみてくださいね。

パターンA:大人気の万能選手『ReactNode』の例

カードのデザインをしてくれる優しいコンポーネントを作ります。中身には、見出しでも、段落でも、ただの文字列でも入れられるようにしたいので、`ReactNode` を採用します。

import React from ‘react’;

// カードコンポーネントのPropsの型定義
type CardProps = {
title: string;
// ここがポイント!テキストもJSXタグも、なんでも受け止めます
children: React.ReactNode;
};

export const Card: React.FC = ({ title, children }) => {
return (

);
};

// 【使い方】
// 文字列だけでも、タグの組み合わせでも、怒られずに美しく表示されます!
export const App = () => {
return (

{/ 中身が「ただの文字列」のパターン /}

明日はメンテナンスのためお休みです。

{/ 中身が「複雑なJSX要素」のパターン /}

名前:すずき


);
};

パターンB:職人肌の厳格派『ReactElement』の例

今度は、特定のJSX要素だけを受け入れたいマニアックなコンポーネントです。「ただの文字列」をうっかり入れようとすると、TypeScriptが「おいおい、それはルール違反だよ!」と赤く波線を出して教えてくれます。

import React, { ReactElement } from ‘react’;

// 専用のアイテムコンポーネント(これだけを許可したい!)
type MenuItemProps = {
label: string;
};
export const MenuItem: React.FC = ({ label }) =>

  • {label}
  • ;

    // リストをまとめる親コンポーネント
    type MenuListProps = {
    // ReactElementを指定することで、「ただの文字列や数値」をこの箱に入れないようにします
    children: ReactElement;
    };

    export const MenuList: React.FC = ({ children }) => {
    return (

      {children}

    );
    };

    // 【使い方】
    export const App = () => {
    return (

    {/ ちゃんと タグを使っているので、大成功! /}

    {/ ⚠️ もしここで「ホーム」というただの文字列を直接書こうとすると、
    TypeScriptが「おい!ReactElementじゃないぞ!」とエラーを出して守ってくれます /}

    );
    };

    —

    まとめ:今日から使える「合言葉」

    いかがでしたでしょうか? 最後に、今日のお話をギュッと縮めておきますね。

    • 基本は `React.ReactNode` を使おう!

    迷ったらこれを選んでおけば間違いありません。コンポーネントの `children` の型で悩んだら、まずコイツを指名しておきましょう。広い心であなたのコードを受け止めてくれます。

    • `React.ReactElement` は「特定のJSXタグだけを厳しく管理したいとき」の秘密兵器!

    ライブラリの内部設計や、特殊なレイアウト制限をかけたいときに出番がやってきます。

    最初は「型なんて面倒くさいな、全部 `any` にしちゃえ!」って思いがちですが(私も昔はそうでした!笑)、TypeScriptがそっとエラーで教えてくれる優しさに気づくと、Reactを書くのが何倍も楽しく、そして頼もしくなりますよ。

    あなたのReactライフが、少しでも晴れやかで楽しいものになりますように。
    つまずいたときは、いつでもまたこのブログに帰ってきてくださいね。それでは、また!

    コメント

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