特定组件的PropTypes?

时间:2016-09-12 18:54:06

标签: javascript reactjs

我有这个简单的React组件:

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

我使用的是这样的:

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

但它失败的道具式验证:

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

为什么?我只在Column内使用ColumnContainerPropTypes.instanceOf(Column)不能像我期望的那样工作吗?我假设如何指定ColumnContainer只接受Column类型的儿童?

2 个答案:

答案 0 :(得分:1)

进行了一些挖掘,提出了基于josephmartin09's solution的辅助函数:

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

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

用法:

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

它并不理想,因为它不支持像'div'这样的原生DOM元素,并且错误信息毫无价值,但它现在还可以。

答案 1 :(得分:0)

我对childrenOf进行了大修,以支持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;
}

用法示例:

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

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