PropTypes of specific component?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP


PropTypes of specific component?



I've got this simple React component:


import {PropTypes} from 'react';
import Column from './Column';

export default function ColumnContainer({children}) {
return (
<div className="cr-column-container">{children}</div>
);
}

let ColumnType = PropTypes.instanceOf(Column);

ColumnContainer.propTypes = {
children: PropTypes.oneOfType([
ColumnType,
PropTypes.arrayOf(ColumnType),
]).isRequired,
};



Which I use like this:


render() {
return (
<ColumnContainer>
<Column>
*snip*
</Column>
</ColumnContainer>
);
}



But it's failing prop-type validation:


Warning: Failed prop type: Invalid prop `children` supplied to `ColumnContainer`.
in ColumnContainer (created by ExampleContainer)
in ExampleContainer



Why is that? I've only used Columns inside of the ColumnContainer. Does PropTypes.instanceOf(Column) not work like I expect? How am I supposed to specify that ColumnContainer will only accept children of type Column?


Column


ColumnContainer


PropTypes.instanceOf(Column)


ColumnContainer


Column





check this out: github.com/facebook/react/issues/2979
– james emanon
Sep 12 '16 at 19:30





@jamesemanon Just found that. None of the solutions presented are ideal though.
– mpen
Sep 12 '16 at 19:41




2 Answers
2



Did some digging, came up with this helper function based on josephmartin09's solution:


export function childrenOf(...types) {
let fieldType = PropTypes.shape({
type: PropTypes.oneOf(types),
});

return PropTypes.oneOfType([
fieldType,
PropTypes.arrayOf(fieldType),
]);
}



Usage:


ColumnContainer.propTypes = {
children: childrenOf(Column).isRequired,
};



It's not ideal because it doesn't support native DOM elements like 'div' and the error message is worthless, but it'll do for now.


'div'



I overhauled childrenOf with support for react-hot-loader 4.x:


childrenOf


import { areComponentsEqual } from 'react-hot-loader';

export function childrenOf(...types) {
return requirable((props, propName, componentName, location, propFullName) => {
const component = props[propName];
if(!location) {
location = 'prop';
}
if(!propFullName) {
propFullName = propName;
}
const check = c => types.some(type => areComponentsEqual(type,c.type));
const valid = Array.isArray(component) ? component.every(check) : check(component);
if(!valid) {
return new Error(
`Invalid ${location} `${propFullName}` supplied to `${componentName}`. Every element must be a <${types.map(t => getDisplayName(t)).join('|')}>.`
);
}
return null;
});
}

function requirable(predicate) {
const propType = (props, propName, ...rest) => {
// don't do any validation if empty
if(props[propName] === undefined) {
return null;
}

return predicate(props, propName, ...rest);
};

propType.isRequired = (props, propName, componentName, ...rest) => {
// warn if empty
if(props[propName] === undefined) {
return new Error(`Required prop `${propName}` was not specified in `${componentName}`.`);
}

return predicate(props, propName, componentName, ...rest);
};

return propType;
}



Usage example:


export function TBody({children, ...attrs}) {
return <tbody {...attrs}>{children}</tbody>;
}

TBody.propTypes = {
children: WxTypes.childrenOf(TRow),
};






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

How to scale/resize CVPixelBufferRef in objective C, iOS

Stripe::AuthenticationError No API key provided. Set your API key using “Stripe.api_key = ”

SVG with two text elements. When one resizes due to textLength - how to resize the other one to the same character size