有时候我们遇到的需求是,有些嵌套父元素的子元素只能是几个特定的组件。
    比如antd中,Breadcrumb 只能往里面放 Breadcrumb.Item、Step只能向里面放Step之、Select只能向里面放Option之类的。我们一定也遇到过,如果放的不对,可能会用warnning或者error。
    比如antd中Breadcrumb的源码中有这样的代码:

    1. devWarning(
    2. element.type &&
    3. (element.type.__ANT_BREADCRUMB_ITEM === true ||
    4. element.type.__ANT_BREADCRUMB_SEPARATOR === true),
    5. 'Breadcrumb',
    6. "Only accepts Breadcrumb.Item and Breadcrumb.Separator as it's children",
    7. );

    element.type.__ANT_BREADCRUMB_ITEM 就是在检验子组件的类型。
    这个东西是怎么来的呢?

    1. // BreadcrumbItem文件的底部定义了这个
    2. BreadcrumbItem.__ANT_BREADCRUMB_ITEM = true;

    但是检验的时候是element.type.__ANT_BREADCRUMB_ITEM是在element.type上。
    下面写了一个简单的DEMO来说明这个问题:

    1. class Father extends React.Component {
    2. render() {
    3. const { children } = this.props;
    4. React.Children.forEach(children, child => {
    5. console.log('child', child);
    6. console.log('child.type', child.type);
    7. console.log('child.type.cjj', child.type.cjj);
    8. console.log('child instanceof RccSon', child instanceof RccSon)
    9. console.log('child instanceof RccSon', child.type instanceof RccSon)
    10. console.log('child instanceof RccSon', child.type === RccSon)
    11. })
    12. return (<div>{children}</div>);
    13. }
    14. }
    15. const RfcSon = () => {
    16. return <p> RfcSon</p>
    17. }
    18. RfcSon.cjj = true
    19. class RccSon extends React.Component {
    20. render = () => (<p>RccSon</p>)
    21. }
    22. RccSon.cjj = true
    23. class MainTest extends React.Component {
    24. render() {
    25. return (
    26. <Father>
    27. <RccSon />
    28. </Father>
    29. );
    30. }
    31. }
    32. ReactDOM.render(<MainTest />, mountNode);

    在父组件的React.Children.forEach中先后校验了child, child.type, child.type.cjj。

    • child是React.Element对象,就是JSX被翻译成virtualDOM的那个对象。这个vDOM对象其实自身的类型是$$typeof: Symbol(react.element)。$$typeof属性是vDOM对象类型的标记。
    • child.type是我们定义的函数式组件也好,类组件也好,最终被编译的js函数。
    • 因为我们的变量是在函数上定义的,那么自然要校验 函数.cjj,就是 child.type.cjj。

    另外,验证类型,最开始其实第一时间想到的是,instanceof能不能校验。但是仔细想下,其实instanceof校验的某类(函数)的实例和其定义类(函数)的关系,追溯整个原型链。

    • 但是我们的child对象,是ReactElement的实例。所以console.log(‘child instanceof RccSon’, child instanceof RccSon)不行false。
    • child.type是我们定义的函数式组件也好,类组件也好,最终被编译的js函数。所以X instanceof X 是false。因为就是那个函数,所以child.type === RccSon才对。