DEV Community

loading...

Counting React Children

boywithsilverwings profile image Agney Menon Originally published at blog.agney.dev ・2 min read

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 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

/**
 * 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);
  }
}

I write a once a month newsletter about catching up with the Web and it's ever evolving developer experiences. Feel free to browse the archives and of course, subscribe

Discussion (1)

pic
Editor guide
Collapse
wesleyjohnson profile image
Wes Johnson • Edited

I've never used either of these, but it's good to know the difference in case I ever do. Thanks for the post!