Children.count()
and Children.toArray().length
have different outputs. We are going to explore how and why.
Consider a React component:
import React, { Children } from "react";
function Wrapper({ children }) {
const count = Children.count(children);
const countArray = Children.toArray(children).length;
return (
<section>
<p>
<pre>Children.count:</pre>
{count}
</p>
<p>
<pre>Children.toArray:</pre>
{countArray}
</p>
{children}
</section>
);
}
export default Wrapper;
Now, to render this component we are giving it:
<Wrapper>
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</Wrapper>
Now, both counts listed in the UI would point to 2. What about if we change the Wrapper children to:
<Wrapper>
<h1>Hello CodeSandbox</h1>
{false && <h2>Start editing to see some magic happen!</h2>}
</Wrapper>
Now, Children.count
will report 2 while Children.toArray().length
will report 1.
This is because Children.count
is counting the number of slots that children has. Whether it be false
, null
or undefined
, it's still a slot and Children.count
does a good job of counting it. From the docs:
number of times that a callback passed to
map
orforEach
would be invoked.
Children.toArray
on the other hand, converts Children it receives to an opaque structure containing only the elements that JSX can render. That's how it reports that there is only a single child inside Wrapper
, the other one is conditionally false.
You often see people try to weed out invalid children using this property. For eg. Chakra UI
/**
* Gets only the valid children of a component,
* and ignores any nullish or falsy child.
*
* @param children the children
*/
export function getValidChildren(children: React.ReactNode) {
return React.Children.toArray(children).filter((child) =>
React.isValidElement(child),
) as React.ReactElement[]
}
Here's a Children.count
usage example from Ant Design:
componentDidUpdate(prevProps: CarouselProps) {
// If the number of Children (even if something was conditionally rendered) has changed, then go to the first slide.
if (React.Children.count(this.props.children) !== React.Children.count(prevProps.children)) {
this.goTo(this.props.initialSlide || 0, false);
}
}
Have fun 🎉