Children
Children
lets you manipulate and transform the JSX you received as the children
prop.
const mappedChildren = Children.map(children, child =>
<div className="Row">
{child}
</div>
);
Reference
Children.count(children)
Call Children.count(children)
to count the number of children in the children
data structure.
import { Children } from 'react';
function RowList({ children }) {
return (
<>
<h1>Total rows: {Children.count(children)}</h1>
...
</>
);
}
Parameters
children
: The value of thechildren
prop received by your component.
Returns
The number of nodes inside these children
.
Caveats
- Empty nodes (
null
,undefined
, and Booleans), strings, numbers, and React elements count as individual nodes. Arrays don’t count as individual nodes, but their children do. The traversal does not go deeper than React elements: they don’t get rendered, and their children aren’t traversed. Fragments don’t get traversed.
Children.forEach(children, fn, thisArg?)
Call Children.forEach(children, fn, thisArg?)
to run some code for each child in the children
data structure.
import { Children } from 'react';
function SeparatorList({ children }) {
const result = [];
Children.forEach(children, (child, index) => {
result.push(child);
result.push(<hr key={index} />);
});
// ...
Parameters
children
: The value of thechildren
prop received by your component.fn
: The function you want to run for each child, similar to the arrayforEach
method callback. It will be called with the child as the first argument and its index as the second argument. The index starts at0
and increments on each call.- optional
thisArg
: Thethis
value with which thefn
function should be called. If omitted, it’sundefined
.
Returns
Children.forEach
returns undefined
.
Caveats
- Empty nodes (
null
,undefined
, and Booleans), strings, numbers, and React elements count as individual nodes. Arrays don’t count as individual nodes, but their children do. The traversal does not go deeper than React elements: they don’t get rendered, and their children aren’t traversed. Fragments don’t get traversed.
Children.map(children, fn, thisArg?)
Call Children.map(children, fn, thisArg?)
to map or transform each child in the children
data structure.
import { Children } from 'react';
function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}
Parameters
children
: The value of thechildren
prop received by your component.fn
: The mapping function, similar to the arraymap
method callback. It will be called with the child as the first argument and its index as the second argument. The index starts at0
and increments on each call. You need to return a React node from this function. This may be an empty node (null
,undefined
, or a Boolean), a string, a number, a React element, or an array of other React nodes.- optional
thisArg
: Thethis
value with which thefn
function should be called. If omitted, it’sundefined
.
Returns
If children
is null
or undefined
, returns the same value.
Otherwise, returns a flat array consisting of the nodes you’ve returned from the fn
function. The returned array will contain all nodes you returned except for null
and undefined
.
Caveats
-
Empty nodes (
null
,undefined
, and Booleans), strings, numbers, and React elements count as individual nodes. Arrays don’t count as individual nodes, but their children do. The traversal does not go deeper than React elements: they don’t get rendered, and their children aren’t traversed. Fragments don’t get traversed. -
If you return an element or an array of elements with keys from
fn
, the returned elements’ keys will be automatically combined with the key of the corresponding original item fromchildren
. When you return multiple elements fromfn
in an array, their keys only need to be unique locally amongst each other.
Children.only(children)
Call Children.only(children)
to assert that children
represent a single React element.
function Box({ children }) {
const element = Children.only(children);
// ...
Parameters
children
: The value of thechildren
prop received by your component.
Returns
If children
is a valid element, returns that element.
Otherwise, throws an error.
Caveats
- This method always throws if you pass an array (such as the return value of
Children.map
) aschildren
. In other words, it enforces thatchildren
is a single React element, not that it’s an array with a single element.
Children.toArray(children)
Call Children.toArray(children)
to create an array out of the children
data structure.
import { Children } from 'react';
export default function ReversedList({ children }) {
const result = Children.toArray(children);
result.reverse();
// ...
Parameters
children
: The value of thechildren
prop received by your component.
Returns
Returns a flat array of elements in children
.
Caveats
- Empty nodes (
null
,undefined
, and Booleans) will be omitted in the returned array. The returned elements’ keys will be calculated from the original elements’ keys and their level of nesting and position. This ensures that flattening the array does not introduce changes in behavior.
Usage
Transforming children
To transform the children JSX that your component receives as the children
prop, call Children.map
:
import { Children } from 'react';
function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}
In the example above, the RowList
wraps every child it receives into a <div className="Row">
container. For example, let’s say the parent component passes three <p>
tags as the children
prop to RowList
:
<RowList>
<p>This is the first item.</p>
<p>This is the second item.</p>
<p>This is the third item.</p>
</RowList>
Then, with the RowList
implementation above, the final rendered result will look like this:
<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
is similar to to transforming arrays with map()
. The difference is that the children
data structure is considered opaque. This means that even if it’s sometimes an array, you should not assume it’s an array or any other particular data type. This is why you should use Children.map
if you need to transform it.
import { Children } from 'react'; export default function RowList({ children }) { return ( <div className="RowList"> {Children.map(children, child => <div className="Row"> {child} </div> )} </div> ); }
Dla dociekliwych
In React, the children
prop is considered an opaque data structure. This means that you shouldn’t rely on how it is structured. To transform, filter, or count children, you should use the Children
methods.
In practice, the children
data structure is often represented as an array internally. However, if there is only a single child, then React won’t create an extra array since this would lead to unnecessary memory overhead. As long as you use the Children
methods instead of directly introspecting the children
prop, your code will not break even if React changes how the data structure is actually implemented.
Even when children
is an array, Children.map
has useful special behavior. For example, Children.map
combines the keys on the returned elements with the keys on the children
you’ve passed to it. This ensures the original JSX children don’t “lose” keys even if they get wrapped like in the example above.
Running some code for each child
Call Children.forEach
to iterate over each child in the children
data structure. It does not return any value and is similar to the array forEach
method. You can use it to run custom logic like constructing your own array.
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; }
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> ); }
Converting children to an array
Call Children.toArray(children)
to turn the children
data structure into a regular JavaScript array. This lets you manipulate the array with built-in array methods like filter
, sort
, or reverse
.
import { Children } from 'react'; export default function ReversedList({ children }) { const result = Children.toArray(children); result.reverse(); return result; }
Alternatives
Exposing multiple components
Manipulating children with the Children
methods often leads to fragile code. When you pass children to a component in JSX, you don’t usually expect the component to manipulate or transform the individual children.
When you can, try to avoid using the Children
methods. For example, if you want every child of RowList
to be wrapped in <div className="Row">
, export a Row
component, and manually wrap every row into it like this:
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> ); }
Unlike using Children.map
, this approach does not wrap every child automatically. However, this approach has a significant benefit compared to the earlier example with Children.map
because it works even if you keep extracting more components. For example, it still works if you extract your own MoreRows
component:
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> </> ); }
This wouldn’t work with Children.map
because it would “see” <MoreRows />
as a single child (and a single row).
Accepting an array of objects as a prop
You can also explicitly pass an array as a prop. For example, this RowList
accepts a rows
array as a 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> } ]} /> ); }
Since rows
is a regular JavaScript array, the RowList
component can use built-in array methods like map
on it.
This pattern is especially useful when you want to be able to pass more information as structured data together with children. In the below example, the TabSwitcher
component receives an array of objects as the 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> } ]} /> ); }
Unlike passing the children as JSX, this approach lets you associate some extra data like header
with each item. Because you are working with the tabs
directly, and it is an array, you do not need the Children
methods.
Calling a render prop to customize rendering
Instead of producing JSX for every single item, you can also pass a function that returns JSX, and call that function when necessary. In this example, the App
component passes a renderContent
function to the TabSwitcher
component. The TabSwitcher
component calls renderContent
only for the selected tab:
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>; }} /> ); }
A prop like renderContent
is called a render prop because it is a prop that specifies how to render a piece of the user interface. However, there is nothing special about it: it is a regular prop which happens to be a function.
Render props are functions, so you can pass information to them. For example, this RowList
component passes the id
and the index
of each row to the renderRow
render prop, which uses index
to highlight even rows:
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> ); }} /> ); }
This is another example of how parent and child components can cooperate without manipulating the children.
Troubleshooting
I pass a custom component, but the Children
methods don’t show its render result
Suppose you pass two children to RowList
like this:
<RowList>
<p>First item</p>
<MoreRows />
</RowList>
If you do Children.count(children)
inside RowList
, you will get 2
. Even if MoreRows
renders 10 different items, or if it returns null
, Children.count(children)
will still be 2
. From the RowList
’s perspective, it only “sees” the JSX it has received. It does not “see” the internals of the MoreRows
component.
The limitation makes it hard to extract a component. This is why alternatives are preferred to using Children
.