React.Children API is used to manipulate props.children in React. For example, React.Children.count can be used to count child nodes up. This Sample component will show Empty when there are no children, but if they exist, it will show them.
1import React, {PropsWithChildren} from "react";23export const Sample: React.FC<PropsWithChildren<unknown>> = ({children}) => (4 <div>5 {React.Children.count(children) === 0 ? (6 <p>Empty</p>7 ) : children}8 </div>9)
Examples
1// passing string2<Sample>3 Hello4</Sample>
Hello
1// passing array2<Sample>3 {[1, 2].map(num => (4 <p key={num}>{num}</p>5 ))}6</Sample>
1
2
1// passing null2<Sample>3 {null}4</Sample>
// Empty
1// passing empty array2<Sample>3 {[].map(num => (4 <p>{num}</p>5 ))}6</Sample>
// Empty
As the above examples show, React.Children.count returns 0 not only for empty array but also null (This also applies to undefined.).
Code Reading
First, check the type of props.children.
@types/react/index.d.ts1type PropsWithChildren<P> = P & { children?: ReactNode };
It is ReactNode, then what is this?
@types/react/index.d.ts1//2// React Nodes3// http://facebook.github.io/react/docs/glossary.html4// ----------------------------------------------------------------------56type ReactText = string | number;7type ReactChild = ReactElement | ReactText;89interface ReactNodeArray extends Array<ReactNode> {}10type ReactFragment = {} | ReactNodeArray;11type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
ReactNode is the union type of many types including primitive types such as string, null and object types such as ReactElement. (Roughly, ReactElement is the type represents JSX) Arrays can be children thanks to ReactFragment.
Next, check the type of argument of React.Children.count function.
@types/react/index.d.ts1//2// React.Children3// ----------------------------------------------------------------------45interface ReactChildren {6 map<T, C>(children: C | C[], fn: (child: C, index: number) => T):7 C extends null | undefined ? C : Array<Exclude<T, boolean | null | undefined>>;8 forEach<C>(children: C | C[], fn: (child: C, index: number) => void): void;9 count(children: any): number;10 only<C>(children: C): C extends any[] ? never : C;11 toArray(children: ReactNode | ReactNode[]): Array<Exclude<ReactNode, boolean | null | undefined>>;12}
React.Children.count receives any!!.
According to this official document, React.Children APIs are in packages/react.
React.Children.count function is here in React.js. This count function seems to be imported from ReactChildren.
React.js1import {forEach, map, count, toArray, only} from './ReactChildren';
Seeing ReactChildren.js, you can see countChildren is exported as count. We found the implementation of React.Children.count.
ReactChildren.js:L2481function countChildren(children: ?ReactNodeList): number {2 let n = 0;3 mapChildren(children, () => {4 n++;5 // Don't return anything6 });7 return n;8}
This function just counts children up using mapChildren. In this function, props.children is converted to array of ReactNode by mapIntoArray.
The implementation of the function is a little bit long. Roughly, it checks the type of children and converts into array. If children is Array, mapIntoArray is called recursively.
Type checking whether children is null, string or Array is performed here.
Whatever the type is, children will be converted to Array. That's why React.Children.count can be used as I introduced first. (Strictly, non-Array objects such as Map, simple object are not supported.)