4

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?

2
  • check this out: github.com/facebook/react/issues/2979 Commented Sep 12, 2016 at 19:30
  • @jamesemanon Just found that. None of the solutions presented are ideal though. Commented Sep 12, 2016 at 19:41

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

Comments

0

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

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), }; 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.