有时候我们遇到的需求是,有些嵌套父元素的子元素只能是几个特定的组件。
比如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 = trueclass RccSon extends React.Component {render = () => (<p>RccSon</p>)}RccSon.cjj = trueclass 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才对。
