Given a component receives a function as a child (callback as a child pattern, also known as render prop pattern):
<Foo>{() => <Bar/>}</Foo>
React.Children.count(props.children) === 0
in Foo
.
The documentation doesn't seem to mention that React.Children
accepts only valid React elements, so the fact that child function is ignored looks odd.
How does React.Children
treat non-element children and why?
References to official sources and/or source code are welcome.
React Child FunctionReact allows for you to specify a function as a child, which children is just a normal prop so it is equivalent to a render callback.
React Components and Children In React, a component can have one, many, or no children.
The React. js error "Objects are not valid as a React child" occurs when we try to directly render an object or an array in our JSX code. To solve the error, use the map() method to render arrays or access properties on the object in your JSX code.
As others have stated, the documentation states that React.Children.count(children)
only returns the count of the number of children that are valid React
Components
.
React.Children
does not ignore other types of children, and if you need to get the count, you only need to determine the length of the array in the root child Object, just like you would in vanilla js. If you look at react-motion, you'll see that they specify that children
must be type of func
:
Mouse.propTypes = { children: PropTypes.func.isRequired };
And they further ensure that there's only one child with React.Children.only
(docs):
render(): ReactElement { const renderedChildren = this.props.children(this.state.currentStyle); return renderedChildren && React.Children.only(renderedChildren); }
React does not handle different types of children on its own, instead, you have to handle them yourself. I put together a code sandbox to show you why.
Disclaimer: It's not a solution but just an eye where we could look at.
I'm not sure but if it is indeed needed to be fixed in React itself, then I would suggest to change in the following function:
React Element
export function isValidElement(object) { return ( typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE ); }
This code:
typeof object === 'object'
To something like:
typeof Object.create(object) === 'object'
And also add a Symbol for such something like:
Symbol.for('react.function')
in the React Symbol.
this.props.children.length
This lets you count the function as child component
as a children. this.props.children
includes any type of element, expressions, or component whilst this.props.children
inside React.Children
function as child is being ignored as children. Continue reading bellow to understand it better...
Here's a demo.
However, I have just submitted an issue and if you wish you can keep following there.
The docs specifies that the React.Children.count
only counts the component in children.
You probably already have known what exactly is children in react.
React takes everything as children except the function as child.
Here's the reference where it states:
React components don't support functions as children.
If you wish you can look deeper here.
So, you have function as a child in <Foo />
component so it does return 0 as it's not being considered as children.
You can optionally count those expressions as its children then you may convert them to array first and then count like:
class CountExpression extends React.Component { render() { const children = React.Children.toArray(this.props.children) return <p>{React.Children.count(children)}</p> } } { /* Counts 2 */ } <CountExpression> {'one'} {'two'} { () => <p>Still, this will be ignored as child and is not included in array</p>} </CountExpression>
Here's a draft demo if you want to have a look.
Look at the following example how children is being counted:
class CountChildren extends React.Component { render() { return <p>{React.Children.count(this.props.children)}</p> } } { /* Renders 1 */ } <CountChildren> Simply a text! </CountChildren> { /* Renders 2 */ } <CountChildren> <p>Html element</p> <ChildComponent /> </CountChildren> { /* Renders 3 */ } <CountChildren> Simply a text! <p>Html element</p> <ChildComponent /> </CountChildren> { /* Renders 3 */ } <CountChildren> Simply a text! <p>Html element</p> <ChildComponent /> { /* ignores it as it's not a component */ } { () => <div>Function as a child component</div> } </CountChildren>
So, you can notice that React can accept any type of children regardless of array, a function, or an object, etc.
If you wish you can also ignore rendering the children checking it with a condition. For eg.:
class SingleChildComponent extends React.Component { render() { return ( <div> { Array.isArray(this.props.children) ? 'Sorry, you can only pass single child!' : this.props.children() } </div> ) } } { /* Renders 'Sorry, you can only pass single child!' */ } <SingleChildComponent> <p>First children</p> <SecondChildren /> </SingleChildComponent> { /* Renders `<p>Single child</p>` */ } <SingleChildComponent> <p>Single child</p> </SingleChildComponent>
If you wish, you can convert the children to array and sort it out like below:
class SortComponent extends React.Component { render() { const children = React.Children.toArray(this.props.children) return <>{ children.sort().join(', ') }</> } } { /* Renders 'Computer, Furniture, Machine' */ } <SortComponent> {'Machine'} { /* First child */ } {'Computer'} { /* Second child */ } {'Furniture'} { /* Third child */ } </SortComponent>
Enforcing a single child:
Bad:
class OnlyChildComponent extends React.Component { render() { return this.props.children() } } { /* Enforcing it as a single child component */ } OnlyChildComponent.propTypes = { children: React.PropTypes.func.isRequired }
If there are more children, then it just shows warning in the console and let the program execute next.
Good:
class OnlyChildComponent extends React.Component { render() { return React.Children.only(this.props.children)() } }
If there are more than one child, then it will throw an error! And it halts the program execution. It's perfect to avoid mess with our component.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With