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
内のノードの数。
注意点
- 空のノード(
null
、undefined
、およびブール値)、文字列、数値、および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.forEach
はundefined
を返します。
注意点 (SVGアイコンは省略)
- 空のノード(
null
、undefined
、およびブール値)、文字列、数値、および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ノードを返す必要があります。これは、空のノード(null
、undefined
、またはブール値)、文字列、数値、React要素、または他のReactノードの配列です。 (配列のmap
メソッドへのリンク)- 省略可能
thisArg
:fn
関数が呼び出される際のthis
値。省略された場合はundefined
です。 (this
値へのリンク)
戻り値 (SVGアイコンは省略)
children
がnull
またはundefined
の場合、同じ値を返します。
それ以外の場合、fn
関数から返されたノードで構成されるフラット配列を返します。返される配列には、null
とundefined
を除く、返されたすべてのノードが含まれます。
注意点 (SVGアイコンは省略)
-
空のノード(
null
、undefined
、およびブール値)、文字列、数値、および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はなぜ常に配列ではないのですか? (リンクアイコン)
children propはなぜ常に配列ではないのですか? (リンクアイコン)
Reactでは、children
propは *不透明* なデータ構造と見なされます。つまり、その構造に依存すべきではありません。子要素の変換、フィルタリング、またはカウントを行うには、Children
メソッドを使用する必要があります。
実際には、children
データ構造は内部的に配列として表現されることがよくあります。ただし、子要素が1つだけの場合、Reactは不要なメモリオーバーヘッドが発生するため、余分な配列を作成しません。children
propを直接イントロスペクトする代わりに、Children
メソッドを使用する限り、Reactがデータ構造の実際の実装方法を変更しても、コードは壊れません。
children
が配列の場合でも、Children.map
には便利な特殊な動作があります。たとえば、Children.map
は、返された要素の キー を、渡した children
のキーと組み合わせます。これにより、上記の例のようにラップされても、元のJSXの子要素がキーを「失う」ことがありません。
各子要素に対して何らかのコードを実行する (リンクアイコン)
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.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.toArray(children)
を呼び出すことで、children
データ構造を通常の JavaScript 配列に変換できます。これにより、filter
、sort
、reverse
などの組み込み配列メソッドを使用して配列を操作できます。
import { Children } from 'react'; export default function ReversedList({ children }) { const result = Children.toArray(children); result.reverse(); return result; }
代替案 ...
複数のコンポーネントを公開する ...
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
コンポーネントは、各行の id
と index
を renderRow
レンダープロップに渡します。 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
を使用するよりも代替手段が推奨されます。