Counting React Children

Posted on June 24, 2020https://blog.agney.dev/react-children-count/

Children.count() and Children.toArray().length have different outputs. We are going to explore how and why.

Consider a React component:

1import React, { Children } from "react";
2
3function Wrapper({ children }) {
4 const count = Children.count(children);
5 const countArray = Children.toArray(children).length;
6 return (
7 <section>
8 <p>
9 <pre>Children.count:</pre>
10 {count}
11 </p>
12 <p>
13 <pre>Children.toArray:</pre>
14 {countArray}
15 </p>
16 {children}
17 </section>
18 );
19}
20
21export 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 or forEach 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

1/**
2 * Gets only the valid children of a component,
3 * and ignores any nullish or falsy child.
4 *
5 * @param children the children
6 */
7export function getValidChildren(children: React.ReactNode) {
8 return React.Children.toArray(children).filter((child) =>
9 React.isValidElement(child),
10 ) as React.ReactElement[]
11}

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 🎉

WebMentions

1 Likes
Dane David
0 Reposts
Buy me a coffeeBuy me a coffee