React の ReactNode とは何か?

TwitterFacebookHatena

TL;DR

このページでは、ReactNodeの実装方法について解説しますね。一言で言うとReactNodeとは、React のコンポーネントのレンダリングに使用できる任意の型を表現するための用語です。

開発環境 バージョン
Next.js 13.4.4
TypeScript 5.0.4
Emotion 11.11.0
React 18.2.0

ReactNode の要点

要点をまとめると以下のようになります。

  1. React コンポーネントが子要素(children)として受け入れることができるデータ型を表す
  2. 文字列、数値、React コンポーネント、配列、null、undefined など、多岐にわたる型を包括する
  3. TypeScript で React コンポーネントを定義する際に、ReactNodeは子要素の型定義としてよく使われる

したがって、ReactNodeを用いてコンポーネントの props を定義すると、そのコンポーネントは任意の子要素を受け入れることができます。この柔軟性が React の強力な機能の一つとなっています。

どのような型を持つことができるか

ReactNode は、React の要素(Element)を表現するための用語で、React のコンポーネントツリーを形成します。ReactNode は次のいずれかの型を持つことができます。

  • boolean
  • null
  • undefined
  • number
  • string
  • ReactElement(React コンポーネント自体)
  • ReactFragment(複数の子要素をまとめる)
  • ReactPortal(レンダリングされる DOM を特定の場所に設定する)
  • Array<ReactNode>(ReactNode の配列)

つまり、ReactNode は非常に柔軟で、文字列や数値といったプリミティブ型から、React コンポーネントまで様々な型を含むことができます。これにより、複雑な UI 構造をツリー形式で表現し、それをブラウザ上でレンダリングすることができます。

type Props = {
  children: React.ReactNode
}

const Component = ({ children }: Props) => {
  return <div>{children}</div>
}

const App = () => {
  return (
    <Component>
      <h1>Hello, world!</h1>
    </Component>
  )
}

この基本的なソースコードでは、Componentという新しいコンポーネントを定義しています。ここでchildrenというプロパティは、ReactNode の型を持ちます。このchildrenは、<Component>タグの開始タグと終了タグの間に配置された任意の React ノードを表します。

interface で ReactNode を定義する

interfaceを使ってReactNodeを含む props を定義することも可能です。ReactNodeは任意の子要素を表す型なので、以下のような形でコンポーネントの props を定義できます。

interface MyComponentProps {
  children: React.ReactNode
}

const MyComponent = ({ children }: MyComponentProps) => {
  return <div>{children}</div>
}

この例では、MyComponentは任意の子要素を受け入れ、それをdiv要素の中でレンダリングします。この子要素はReactNode型として定義されているので、テキスト、他の React コンポーネント、配列など、あらゆる種類の React 要素を受け入れます。

children として受け入れることができるデータ型

ReactNodeは React コンポーネントが子要素(children)として受け入れることができるデータ型を表現します。子要素とは、コンポーネントを利用する際にその開始タグと終了タグの間に配置される要素のことです。

<Component>{/* ここに配置される要素が子要素(children) */}</Component>

React の子要素は非常に柔軟で、単純な文字列や数値から、React コンポーネント自体、配列、null や undefined まで、様々な型を含むことができます。これらすべての可能性を包含する型としてReactNodeが提供されています。

例えば、以下のようなMyComponentコンポーネントを考えてみましょう。

type Props = {
  children: React.ReactNode
}

const MyComponent = ({ children }: Props) => {
  return <div>{children}</div>
}

const App = () => {
  return (
    <MyComponent>
      <h1>Hello, world!</h1>
      <p>Welcome to my app.</p>
    </MyComponent>
  )
}

上記の例では、MyComponentchildrenという名前の props を受け入れます。このchildrenReactNode型で、<MyComponent>タグの開始タグと終了タグの間にあるすべての内容を含みます。この場合、<h1>Hello, world!</h1><p>Welcome to my app.</p>がその子要素となります。

ReactChild との違い

ReactChildReactNodeは、React の型システムにおける重要な型で、それぞれ異なる種類の React 要素を表現します。

ReactChild

ReactChildは、React コンポーネントがレンダリングできる要素のうち最も基本的な型を表します。具体的には、ReactChildReactElementまたはstringまたはnumber型を取ります。

つまり、単一の React コンポーネント、文字列、または数値はReactChildとして扱うことができます。

ReactNode

一方で、ReactNodeは React がレンダリング可能なすべての種類の値を表します。具体的には、ReactNodeは以下の値を取ることができます。

  • ReactChild(すなわち、ReactElementstringnumber
  • boolean(通常、これは意図的にレンダリングされませんが、条件付きレンダリングの結果として生じることがあります)
  • null(意図的に何もレンダリングしないことを示す)
  • undefined(意図的に何もレンダリングしないことを示す)
  • ReactFragment(複数の子要素をグループ化する)
  • ReactPortal(コンポーネントツリーの外部にレンダリングする)
  • 配列(これは複数のReactNode要素のリストを指す)

したがって、ReactNodeReactChildよりも幅広い範囲の要素をカバーします。これは、ある React コンポーネントのchildrenprops の型としてよく使われます。これは、そのコンポーネントが任意の子要素(文字列、別のコンポーネント、配列、など)を受け入れることができることを示します。

ReactText

ReactTextは React の型システムにおける基本的な型で、文字列 (string) または数値 (number) のどちらかを表します。これは、React がテキストノードとしてレンダリングできる唯二の JavaScript の原始的なデータ型を表しています。

具体的には、次のようなコンポーネントがあります。

const MyComponent = () => {
  return (
    <div>
      Hello, world! // これはReactTextです (string)
      {2023} // これもReactTextです (number)
    </div>
  )
}

上記の例では、"Hello, world!"と 2023 は、どちらもReactTextとして扱われます。これらは、どちらもブラウザにレンダリングされた時にはテキストノードとして表示されます。

それに対して、ReactChildReactTextReactElementのどちらかを表し、ReactNodeReactChildbooleannullundefinedReactFragmentReactPortal、または配列を表します。したがって、これらの型は React の要素や子要素を扱う際の複雑さを表現するために存在します。

とどのつまり、ReactNodeは最も包括的な型であり、React コンポーネントの子要素として渡すことができるほとんど全てのものをカバーします。つまり、ReactElement(これが一般的な JSX 要素です)、文字列、数値、boolean、null、undefined、React フラグメント、React ポータル、そしてこれら全てを含む配列などが含まれます。

したがって、React コンポーネントの子要素として許容したいもの全てを許容する場合には、ReactNodeを使用すると良いでしょう。ただし、特定の型の子要素のみを許容したい場合(例えば、特定の React コンポーネントのみ、または文字列のみなど)には、より具体的な型を使用することもあります。

Next.js で、ReactNode を実装

続いて Next.js のコンテクストで、ReactNode を用いた実装例を見ていきましょう。

/components/MyComponent.tsx:

import React from 'react'

type Props = {
  children: React.ReactNode
}

const MyComponent = ({ children }: Props) => {
  return <div>{children}</div>
}

export default MyComponent

上記のMyComponent.tsxでは、ReactNode を活用したコンポーネントを定義しています。ReactNode 型のchildrenプロパティは、<MyComponent>タグの開始タグと終了タグの間に配置された任意の React ノードを表現します。

次に、このMyComponentを使ってページコンポーネントを作成します。

/pages/index.tsx:

import MyComponent from '../components/MyComponent'

const HomePage = () => {
  return (
    <MyComponent>
      <h1>Welcome to My Site</h1>
      <p>This is a sample text.</p>
    </MyComponent>
  )
}

export default HomePage

React の ReactNode とは何か?