camden
camden

Reputation: 2026

Specifying complex PropTypes for children

I'm writing a small Toolbar component in React. Here's how it should be used:

<Toolbar>
    <ToolbarButtonSearch />
    <ToolbarButtonFold />
</Toolbar>

or

<Toolbar>
    <ToolbarButtonSearch />
</Toolbar>

or

<Toolbar>
    <ToolbarButtonFold />
</Toolbar>

I'd like to be able to specify that the only children Toolbar accepts are one of ToolbarButtonSearch, one of ToolbarButtonFold, or one of each.

Here's what I have now (not including imports):

export default class Toolbar extends React.Component {

    static get propTypes() {
        const acceptedChildren =
            React.PropTypes.oneOfType([
                React.PropTypes.instanceOf(ToolbarButtonFold),
                React.PropTypes.instanceOf(ToolbarButtonSearch)
            ]);

        return {
            children: React.PropTypes.oneOfType([
                React.PropTypes.arrayOf(acceptedChildren),
                acceptedChildren
            ]).isRequired
        };
    }

    render() {
        return (
            <div className="toolbar">
                {this.props.children}
            </div>
        );
    }
}

Here's how I'm using it:

<Toolbar>
    <ToolbarButtonFold />
</Toolbar>

This results in the following error:

Warning: Failed prop type: Invalid prop 'children' supplied to 'Toolbar'.

I'm not sure what I'm doing wrong. Any insight would be very helpful. Thanks!

Upvotes: 0

Views: 648

Answers (2)

Diego Faria
Diego Faria

Reputation: 9185

Did you tried to do:

customProp: function (props, propName, componentName) {
    props.children.forEach(child => {
        if (!child instanceof ToolbarButtonSearch && !child instanceof ToolbarButtonFold) {
            return new Error(
                'Invalid children supplied to' +
                ' `' + componentName + '`. Validation failed.'
            )
        }
    })
}

Upvotes: 1

Lance Jernigan
Lance Jernigan

Reputation: 226

Instead of passing components (since they have to be imported into Toolbar, anyway), you could just pass props defining if the specific component should be displayed.

const Toolbar = ({showButtonSearch = false, showButtonFold = false, buttonSearchProps = {}, buttonFoldProps = {}}) => {

  return (

    <div className='Toolbar'>

      {showButtonSearch ? <ButtonSearch {...buttonSearchProps} /> : null}

      {showButtonFold ? <ButtonFold {...buttonFoldProps} /> : null}

    </div>

  )

}

This method would also allow you to pass props for the children as well as any further functionality you need before displaying the children (such as ordering the children a specific way).

Upvotes: 1

Related Questions