落とし穴

Childrenの使用は一般的ではなく、脆弱なコードにつながる可能性があります。 一般的な代替案をご覧ください。

Childrenを使用すると、children propとして受け取ったJSXを操作および変換できます。

const mappedChildren = Children.map(children, child =>
<div className="Row">
{child}
</div>
);

リファレンス

Children.count(children)

childrenデータ構造内の子要素の数をカウントするには、Children.count(children)を呼び出します。

import { Children } from 'react';

function RowList({ children }) {
return (
<>
<h1>Total rows: {Children.count(children)}</h1>
...
</>
);
}

以下の例をご覧ください。

パラメータ

  • children: コンポーネントが受け取るchildren propの値。

戻り値

これらのchildren内のノードの数。

注意点

  • 空のノード(nullundefined、およびブール値)、文字列、数値、およびReact要素は、個々のノードとしてカウントされます。配列は個々のノードとしてはカウントされませんが、その子要素はカウントされます。 トラバーサルはReact要素より深くは進みません。つまり、React要素はレンダリングされず、その子要素はトラバースされません。 フラグメントはトラバースされません。

Children.forEach(children, fn, thisArg?)

childrenデータ構造内の各子に対して何らかのコードを実行するには、Children.forEach(children, fn, thisArg?)を呼び出します。

import { Children } from 'react';

function SeparatorList({ children }) {
const result = [];
Children.forEach(children, (child, index) => {
result.push(child);
result.push(<hr key={index} />);
});
// ...

以下の例をご覧ください。

パラメータ (SVGアイコンは省略)

  • children: コンポーネントが受け取るchildren propの値。
  • fn: 各子に対して実行する関数。配列のforEachメソッドのコールバックと同様です。第1引数として子、第2引数としてそのインデックスが渡されます。インデックスは0から始まり、呼び出しごとに増加します。 (配列の forEach メソッドへのリンク)
  • 省略可能 thisArg: fn関数が呼び出される際のthis値。省略された場合はundefinedです。 (thisへのリンク)

戻り値 (SVGアイコンは省略)

Children.forEachundefinedを返します。

注意点 (SVGアイコンは省略)

  • 空のノード(nullundefined、およびブール値)、文字列、数値、およびReact要素は、個々のノードとしてカウントされます。配列は個々のノードとしてはカウントされませんが、その子要素はカウントされます。 トラバーサルはReact要素より深くは進みません。つまり、React要素はレンダリングされず、その子要素はトラバースされません。 フラグメントはトラバースされません。

Children.map(children, fn, thisArg?) (SVGアイコンは省略)

childrenデータ構造内の各子をマップまたは変換するには、Children.map(children, fn, thisArg?)を呼び出します。

import { Children } from 'react';

function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}

以下の例をご覧ください。

パラメータ (SVGアイコンは省略)

  • children: コンポーネントが受け取るchildren propの値。
  • fn: マッピング関数。配列のmapメソッドのコールバックと同様です。第1引数として子、第2引数としてそのインデックスが渡されます。インデックスは0から始まり、呼び出しごとに増加します。この関数からはReactノードを返す必要があります。これは、空のノード(nullundefined、またはブール値)、文字列、数値、React要素、または他のReactノードの配列です。 (配列の map メソッドへのリンク)
  • 省略可能 thisArg: fn関数が呼び出される際のthis値。省略された場合はundefinedです。 (thisへのリンク)

戻り値 (SVGアイコンは省略)

childrennullまたはundefinedの場合、同じ値を返します。

それ以外の場合、fn関数から返されたノードで構成されるフラット配列を返します。返される配列には、nullundefinedを除く、返されたすべてのノードが含まれます。

注意点 (SVGアイコンは省略)

  • 空のノード(nullundefined、およびブール値)、文字列、数値、およびReact要素は、個々のノードとしてカウントされます。配列は個々のノードとしてはカウントされませんが、その子要素はカウントされます。 トラバーサルはReact要素より深くは進みません。つまり、React要素はレンダリングされず、その子要素はトラバースされません。 フラグメントはトラバースされません。

  • もし`fn`から要素、またはキーを持つ要素の配列を返却する場合、**返却された要素のキーは`children`の対応する元のアイテムのキーと自動的に結合されます。** `fn`から配列で複数の要素を返却する場合、それらのキーはそれぞれの中でローカルに一意である必要があります。


`Children.only(children)`

`children`が単一のReact要素を表すことをアサートするために、`Children.only(children)`を呼び出します。

function Box({ children }) {
const element = Children.only(children);
// ...

パラメータ ...

  • children: コンポーネントが受け取るchildren propの値。

戻り値 ...

`children`が有効な要素である場合、その要素を返します。

そうでない場合は、エラーを発生させます。

注意点 ...

  • このメソッドは、**配列(`Children.map`の戻り値など)を`children`として渡すと、常にエラーを発生させます。** つまり、`children`が単一の要素を持つ配列ではなく、単一のReact要素であることを強制します。

`Children.toArray(children)` ...

`children`データ構造から配列を作成するために、`Children.toArray(children)`を呼び出します。

import { Children } from 'react';

export default function ReversedList({ children }) {
const result = Children.toArray(children);
result.reverse();
// ...

パラメータ ...

  • children: コンポーネントが受け取るchildren propの値。

戻り値 ...

`children`内の要素のフラットな配列を返します。

注意点 ...

  • 空のノード(`null`、`undefined`、およびブール値)は、返される配列から省略されます。**返される要素のキーは、元の要素のキーと、それらのネストレベルと位置から計算されます。** これにより、配列のフラット化によって動作が変更されないことが保証されます。

使用方法 ...

子要素の変換 ...

コンポーネントが children propとして受け取る子JSXを変換するには、Children.map を呼び出します。

import { Children } from 'react';

function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}

上記の例では、RowList は受け取ったすべての子要素を <div className="Row"> コンテナでラップします。たとえば、親コンポーネントが3つの <p> タグを children propとして RowList に渡すとします。

<RowList>
<p>This is the first item.</p>
<p>This is the second item.</p>
<p>This is the third item.</p>
</RowList>

上記の RowList の実装では、最終的にレンダリングされる結果は次のようになります。

<div className="RowList">
<div className="Row">
<p>This is the first item.</p>
</div>
<div className="Row">
<p>This is the second item.</p>
</div>
<div className="Row">
<p>This is the third item.</p>
</div>
</div>

Children.map は、map() を使用した配列の変換に似ています。違いは、children データ構造が *不透明* と見なされることです。これは、配列である場合もありますが、配列または他の特定のデータ型であると想定すべきではないことを意味します。そのため、変換する必要がある場合は、Children.map を使用する必要があります。

import { Children } from 'react';

export default function RowList({ children }) {
  return (
    <div className="RowList">
      {Children.map(children, child =>
        <div className="Row">
          {child}
        </div>
      )}
    </div>
  );
}

詳細解説

children propはなぜ常に配列ではないのですか? (リンクアイコン)

Reactでは、children propは *不透明* なデータ構造と見なされます。つまり、その構造に依存すべきではありません。子要素の変換、フィルタリング、またはカウントを行うには、Children メソッドを使用する必要があります。

実際には、children データ構造は内部的に配列として表現されることがよくあります。ただし、子要素が1つだけの場合、Reactは不要なメモリオーバーヘッドが発生するため、余分な配列を作成しません。children propを直接イントロスペクトする代わりに、Children メソッドを使用する限り、Reactがデータ構造の実際の実装方法を変更しても、コードは壊れません。

children が配列の場合でも、Children.map には便利な特殊な動作があります。たとえば、Children.map は、返された要素の キー を、渡した children のキーと組み合わせます。これにより、上記の例のようにラップされても、元のJSXの子要素がキーを「失う」ことがありません。

落とし穴

children データ構造には、JSXとして渡すコンポーネントの **レンダリングされた出力は含まれません**。以下の例では、RowList が受け取る children には、3つではなく2つのアイテムのみが含まれています。

  1. <p>これは最初のアイテムです。</p>
  2. <MoreRows />

この例では、2つの行ラッパーのみが生成されるのはこのためです。

import RowList from './RowList.js';

export default function App() {
  return (
    <RowList>
      <p>This is the first item.</p>
      <MoreRows />
    </RowList>
  );
}

function MoreRows() {
  return (
    <>
      <p>This is the second item.</p>
      <p>This is the third item.</p>
    </>
  );
}

<MoreRows /> のような **内部コンポーネントのレンダリングされた出力を取得する方法はありません**。children を操作する場合。そのため、通常は代替ソリューションのいずれかを使用する方が良いでしょう。


各子要素に対して何らかのコードを実行する (リンクアイコン)

children データ構造の各子要素を反復処理するには、Children.forEach を呼び出します。値を返さず、配列の forEach メソッドに似ています。独自の配列を構築するなど、カスタムロジックを実行するために使用できます。

import { Children } from 'react';

export default function SeparatorList({ children }) {
  const result = [];
  Children.forEach(children, (child, index) => {
    result.push(child);
    result.push(<hr key={index} />);
  });
  result.pop(); // Remove the last separator
  return result;
}

落とし穴

前述のように、children を操作する場合、内部コンポーネントのレンダリングされた出力を取得する方法はありません。そのため、通常は代替ソリューションのいずれかを使用する方が良いでしょう。


子要素をカウントする (リンクアイコン)

子要素の数を計算するには、Children.count(children) を呼び出します。

import { Children } from 'react';

export default function RowList({ children }) {
  return (
    <div className="RowList">
      <h1 className="RowListHeader">
        Total rows: {Children.count(children)}
      </h1>
      {Children.map(children, child =>
        <div className="Row">
          {child}
        </div>
      )}
    </div>
  );
}

落とし穴

前述のように、children を操作する場合、内部コンポーネントのレンダリングされた出力を取得する方法はありません。そのため、通常は代替ソリューションのいずれかを使用する方が良いでしょう。


子要素を配列に変換する

Children.toArray(children) を呼び出すことで、children データ構造を通常の JavaScript 配列に変換できます。これにより、filtersortreverse などの組み込み配列メソッドを使用して配列を操作できます。

import { Children } from 'react';

export default function ReversedList({ children }) {
  const result = Children.toArray(children);
  result.reverse();
  return result;
}

落とし穴

前述のように、children を操作する場合、内部コンポーネントのレンダリングされた出力を取得する方法はありません。そのため、通常は代替ソリューションのいずれかを使用する方が良いでしょう。


代替案 ...

注意

このセクションでは、以下のようにインポートされる Children API(C が大文字)の代替案について説明します。

import { Children } from 'react';

これを、 children propc が小文字)を使用することと混同しないでください。 children prop の使用は推奨されています。

複数のコンポーネントを公開する ...

Children メソッドを使用して子を操作すると、壊れやすいコードになることがよくあります。 JSX でコンポーネントに子を渡す場合、通常、コンポーネントが個々の子を操作または変換することを期待しません。

可能な限り、Children メソッドの使用は避けてください。たとえば、RowList のすべての子を <div className="Row"> でラップする場合、Row コンポーネントをエクスポートし、次のように手動で各行をラップします。

import { RowList, Row } from './RowList.js';

export default function App() {
  return (
    <RowList>
      <Row>
        <p>This is the first item.</p>
      </Row>
      <Row>
        <p>This is the second item.</p>
      </Row>
      <Row>
        <p>This is the third item.</p>
      </Row>
    </RowList>
  );
}

Children.map を使用する場合とは異なり、このアプローチではすべての子が自動的にラップされるわけではありません。**ただし、このアプローチには、 Children.map を使用した前の例 と比較して大きな利点があります。それは、さらにコンポーネントを抽出する場合でも機能するためです。** たとえば、独自の MoreRows コンポーネントを抽出した場合でも機能します。

import { RowList, Row } from './RowList.js';

export default function App() {
  return (
    <RowList>
      <Row>
        <p>This is the first item.</p>
      </Row>
      <MoreRows />
    </RowList>
  );
}

function MoreRows() {
  return (
    <>
      <Row>
        <p>This is the second item.</p>
      </Row>
      <Row>
        <p>This is the third item.</p>
      </Row>
    </>
  );
}

これは、Children.map では機能しません。 <MoreRows /> が単一の子(および単一の行)として「認識」されるためです。


オブジェクトの配列を prop として受け入れる ...

配列を prop として明示的に渡すこともできます。たとえば、この RowList は、rows 配列を prop として受け入れます。

import { RowList, Row } from './RowList.js';

export default function App() {
  return (
    <RowList rows={[
      { id: 'first', content: <p>This is the first item.</p> },
      { id: 'second', content: <p>This is the second item.</p> },
      { id: 'third', content: <p>This is the third item.</p> }
    ]} />
  );
}

rows は通常の JavaScript 配列であるため、RowList コンポーネントは、map などの組み込み配列メソッドを使用できます。

このパターンは、構造化データとして子と一緒に追加情報を渡したい場合に特に役立ちます。以下の例では、TabSwitcher コンポーネントは、tabs prop としてオブジェクトの配列を受け取ります。

import TabSwitcher from './TabSwitcher.js';

export default function App() {
  return (
    <TabSwitcher tabs={[
      {
        id: 'first',
        header: 'First',
        content: <p>This is the first item.</p>
      },
      {
        id: 'second',
        header: 'Second',
        content: <p>This is the second item.</p>
      },
      {
        id: 'third',
        header: 'Third',
        content: <p>This is the third item.</p>
      }
    ]} />
  );
}

子を JSX として渡す場合とは異なり、このアプローチでは、header などの追加データを各アイテムに関連付けることができます。 tabs を直接操作しており、それが配列であるため、Children メソッドは必要ありません。


レンダー prop を呼び出してレンダリングをカスタマイズする ...

個々のアイテムごとに JSX を生成する代わりに、JSX を返す関数を作成し、必要なときにその関数を呼び出すこともできます。この例では、App コンポーネントは、renderContent 関数を TabSwitcher コンポーネントに渡します。TabSwitcher コンポーネントは、選択されたタブに対してのみ renderContent を呼び出します。

import TabSwitcher from './TabSwitcher.js';

export default function App() {
  return (
    <TabSwitcher
      tabIds={['first', 'second', 'third']}
      getHeader={tabId => {
        return tabId[0].toUpperCase() + tabId.slice(1);
      }}
      renderContent={tabId => {
        return <p>This is the {tabId} item.</p>;
      }}
    />
  );
}

renderContent のようなプロップは、ユーザーインターフェースの一部をどのようにレンダリングするかを指定するプロップであるため、*レンダープロップ*と呼ばれます。ただし、特別なことは何もありません。これは、たまたま関数である通常のプロップです。

レンダープロップは関数なので、情報を渡すことができます。たとえば、この RowList コンポーネントは、各行の idindexrenderRow レンダープロップに渡します。 renderRow は、偶数行を強調表示するために index を使用します。

import { RowList, Row } from './RowList.js';

export default function App() {
  return (
    <RowList
      rowIds={['first', 'second', 'third']}
      renderRow={(id, index) => {
        return (
          <Row isHighlighted={index % 2 === 0}>
            <p>This is the {id} item.</p>
          </Row> 
        );
      }}
    />
  );
}

これは、親コンポーネントと子コンポーネントが、子を操作することなく連携できる方法のもう1つの例です。


トラブルシューティング ...

カスタムコンポーネントを渡しますが、Children メソッドはそのレンダリング結果を表示しません ...

このように2つの子を RowList に渡すとします。

<RowList>
<p>First item</p>
<MoreRows />
</RowList>

RowList 内で Children.count(children) を実行すると、2 が取得されます。MoreRows が10個の異なるアイテムをレンダリングする場合でも、null を返す場合でも、Children.count(children) は引き続き 2 になります。RowList の観点からは、受信した JSX のみが「見え」ます。MoreRows コンポーネントの内部は「見え」ません。

この制限により、コンポーネントの抽出が困難になります。そのため、Children を使用するよりも代替手段が推奨されます。