I have a Card
component and a CardGroup
component, and I\'d like to throw an error when CardGroup
has children that aren\'t Card
Use the React.Children.forEach
method to iterate over the children and use the name
property to check the type:
React.Children.forEach(this.props.children, (child) => {
if (child.type.name !== Card.name) {
console.error("Only card components allowed as children.");
}
}
I recommend to use Card.name
instead of 'Card'
string for better maintenance and stability in respect to uglify.
See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
For me the simplest way to achieve this was following code.
Example 1:
import React, {Children} from 'react';
function myComponent({children}) {
return (
<div>{children && Children.map(children, child => {
if (child.type === 'div') return child
})}</div>
)
}
export default myComponent;
Example 2 - With Component
import React, {Children} from 'react';
function myComponent({children}) {
return (
<div>{children && Children.map(children, child => {
if (child.type.displayName === 'Card') return child
})}</div>
)
}
export default myComponent;
For React 0.14+ and using ES6 classes, the solution will look like:
class CardGroup extends Component {
render() {
return (
<div>{this.props.children}</div>
)
}
}
CardGroup.propTypes = {
children: function (props, propName, componentName) {
const prop = props[propName]
let error = null
React.Children.forEach(prop, function (child) {
if (child.type !== Card) {
error = new Error('`' + componentName + '` children should be of type `Card`.');
}
})
return error
}
}
One has to use "React.isValidElement(child)" along with "child.type" if one is working with Typescript in order to avoid type mismatch errors.
React.Children.forEach(props.children, (child, index) => {
if (React.isValidElement(child) && child.type !== Card) {
error = new Error(
'`' + componentName + '` only accepts children of type `Card`.'
);
}
});
You can use a custom propType function to validate children, since children are just props. I also wrote an article on this, if you want more details.
var CardGroup = React.createClass({
propTypes: {
children: function (props, propName, componentName) {
var error;
var prop = props[propName];
React.Children.forEach(prop, function (child) {
if (child.type.displayName !== 'Card') {
error = new Error(
'`' + componentName + '` only accepts children of type `Card`.'
);
}
});
return error;
}
},
render: function () {
return (
<div>{this.props.children}</div>
);
}
});
You can add a prop to your Card
component and then check for this prop in your CardGroup
component. This is the safest way to achieve this in React.
This prop can be added as a defaultProp so it's always there.
class Card extends Component {
static defaultProps = {
isCard: true,
}
render() {
return (
<div>A Card</div>
)
}
}
class CardGroup extends Component {
render() {
for (child in this.props.children) {
if (!this.props.children[child].props.isCard){
console.error("Warning CardGroup has a child which isn't a Card component");
}
}
return (
<div>{this.props.children}</div>
)
}
}
Checking for whether the Card component is indeed a Card component by using type
or displayName
is not safe as it may not work during production use as indicated here: https://github.com/facebook/react/issues/6167#issuecomment-191243709