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
static propTypes = {
children : (props, propName, componentName) => {
const prop = props[propName];
return React.Children
.toArray(prop)
.find(child => child.type !== Card) && new Error(`${componentName} only accepts "<Card />" elements`);
},
}
To validate correct children component i combine the use of react children foreach and the Custom validation proptypes, so at the end you can have the following:
HouseComponent.propTypes = {
children: PropTypes.oneOfType([(props, propName, componentName) => {
let error = null;
const validInputs = [
'Mother',
'Girlfried',
'Friends',
'Dogs'
];
// Validate the valid inputs components allowed.
React.Children.forEach(props[propName], (child) => {
if (!validInputs.includes(child.type.name)) {
error = new Error(componentName.concat(
' children should be one of the type:'
.concat(validInputs.toString())
));
}
});
return error;
}]).isRequired
};
As you can see is having and array with the name of the correct type.
On the other hand there is also a function called componentWithName from the airbnb/prop-types library that helps to have the same result. Here you can see more details
HouseComponent.propTypes = {
children: PropTypes.oneOfType([
componentWithName('SegmentedControl'),
componentWithName('FormText'),
componentWithName('FormTextarea'),
componentWithName('FormSelect')
]).isRequired
};
Hope this help some one :)
I made a custom PropType for this that I call equalTo
. You can use it like this...
class MyChildComponent extends React.Component { ... }
class MyParentComponent extends React.Component {
static propTypes = {
children: PropTypes.arrayOf(PropTypes.equalTo(MyChildComponent))
}
}
Now, MyParentComponent
only accepts children that are MyChildComponent
. You can check for html elements like this...
PropTypes.equalTo('h1')
PropTypes.equalTo('div')
PropTypes.equalTo('img')
...
Here is the implementation...
React.PropTypes.equalTo = function (component) {
return function validate(propValue, key, componentName, location, propFullName) {
const prop = propValue[key]
if (prop.type !== component) {
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
};
}
You could easily extend this to accept one of many possible types. Maybe something like...
React.PropTypes.equalToOneOf = function (arrayOfAcceptedComponents) {
...
}
For those like me, using the TypeScript version. You can filter/modify components like this:
this.modifiedChildren = React.Children.map(children, child => {
if (React.isValidElement(child) && (child as React.ReactElement<any>).type === Card) {
let modifiedChild = child as React.ReactElement<any>;
// Modifying here
return modifiedChild;
}
// Returning other components / string.
// Delete next line in case you dont need them.
return child;
});
Assert the type:
props.children.forEach(child =>
console.assert(
child.type.name == "CanvasItem",
"CanvasScroll can only have CanvasItem component as children."
)
)
Considered multiple proposed approaches, but they all turned out to be either unreliable or overcomplicated to serve as a boilerplate. Settled on the following implementation.
class Card extends Component {
// ...
}
class CardGroup extends Component {
static propTypes = {
children: PropTypes.arrayOf(
(propValue, key, componentName) => (propValue[key].type !== Card)
? new Error(`${componentName} only accepts children of type ${Card.name}.`)
: null
)
}
// ...
}
Here're the key ideas:
PropTypes.arrayOf()
instead of looping over childrenpropValue[key].type !== Card
in a custom validator${Card.name}
to not hard-code the type nameLibrary react-element-proptypes implements this in ElementPropTypes.elementOfType()
:
import ElementPropTypes from "react-element-proptypes";
class CardGroup extends Component {
static propTypes = {
children: PropTypes.arrayOf(ElementPropTypes.elementOfType(Card))
}
// ...
}