有时候我们遇到的需求是,有些嵌套父元素的子元素只能是几个特定的组件。
比如antd中,Breadcrumb 只能往里面放 Breadcrumb.Item、Step只能向里面放Step之、Select只能向里面放Option之类的。我们一定也遇到过,如果放的不对,可能会用warnning或者error。
比如antd中Breadcrumb的源码中有这样的代码:
devWarning(
element.type &&
(element.type.__ANT_BREADCRUMB_ITEM === true ||
element.type.__ANT_BREADCRUMB_SEPARATOR === true),
'Breadcrumb',
"Only accepts Breadcrumb.Item and Breadcrumb.Separator as it's children",
);
element.type.__ANT_BREADCRUMB_ITEM 就是在检验子组件的类型。
这个东西是怎么来的呢?
// BreadcrumbItem文件的底部定义了这个
BreadcrumbItem.__ANT_BREADCRUMB_ITEM = true;
但是检验的时候是element.type.__ANT_BREADCRUMB_ITEM是在element.type上。
下面写了一个简单的DEMO来说明这个问题:
class Father extends React.Component {
render() {
const { children } = this.props;
React.Children.forEach(children, child => {
console.log('child', child);
console.log('child.type', child.type);
console.log('child.type.cjj', child.type.cjj);
console.log('child instanceof RccSon', child instanceof RccSon)
console.log('child instanceof RccSon', child.type instanceof RccSon)
console.log('child instanceof RccSon', child.type === RccSon)
})
return (<div>{children}</div>);
}
}
const RfcSon = () => {
return <p> RfcSon</p>
}
RfcSon.cjj = true
class RccSon extends React.Component {
render = () => (<p>RccSon</p>)
}
RccSon.cjj = true
class MainTest extends React.Component {
render() {
return (
<Father>
<RccSon />
</Father>
);
}
}
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才对。