react-instantiateReactComponent

简况

instantiateReactComponent是在ReactReconciler中被经常引用的一个函数,用于调度并返回组件实例。

目标路径:src/renderers/shared/stack/reconciler/instantiateReactComponent.js

关于这个函数有使用注释,移除DEV和一些警告抛错类函数调用,它大致是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**

- Given a ReactNode, create an instance that will actually be mounted.
*
- @param {ReactNode} node
- @param {boolean} shouldHaveDebugID
- @return {object} A new instance of the element's constructor.
- @protected
*/
function instantiateReactComponent(node, shouldHaveDebugID) {
var instance;
if (node === null || node === false) {
instance = ReactEmptyComponent.create(instantiateReactComponent);
} else if (typeof node === 'object') {
var element = node;
var type = element.type;
if (typeof type !== 'function' && typeof type !== 'string') {
var info = '';
info += getDeclarationErrorAddendum(element._owner);
}
if (typeof element.type === 'string') {
instance = ReactHostComponent.createInternalComponent(element);
} else if (isInternalComponentType(element.type)) {
instance = new element.type(element);
if (!instance.getHostNode) {
instance.getHostNode = instance.getNativeNode;
}
} else {
instance = new ReactCompositeComponentWrapper(element);
}
} else if (typeof node === 'string' || typeof node === 'number') {
instance = ReactHostComponent.createInstanceForText(node);
} else {
// 略
}
instance._mountIndex = 0;
instance._mountImage = null;
return instance;
}

它返回的实例有以下几种:

  • ReactEmptyComponent.create -> ReactDOMEmptyComponent实例
  • ReactHostComponent.createInternalComponent -> ReactDOMComponent实例
  • new element.type(element)
  • new ReactCompositeComponentWrapper(element) -> ReactCompositeComponentWrapper实例

这几个实例都有一个共性。那就是他们全都在原型上定义了mountComponent、receiveComponent、getHostNode、unmountComponent四个方法。这也是ReactReconciler模块的规划需要。

这里有个补充,后来我在<<深入react技术栈>>看到一个小结,说是高屋建瓴也没错,这里可以作为补充:

image-20190617090423902

参数和返回

前面提到的实例类型的返回取决于它的参数,这里一共有四种情况。

type末节点调用
null 或 falseReactEmptyComponent.create
stringReactDOMComponent
function && element.type原生组件element.type(element)
function && element.type自定义组件new ReactCompositeComponentWrapper(element)

ReactEmptyComponent

当node传入值为null 或者 false时候回返回它。

1
2
3
4
5
6
7
8
9
var ReactDOMEmptyComponent = function(instantiate) {
// ReactCompositeComponent uses this:
this._currentElement = null;
// ReactDOMComponentTree uses these:
this._hostNode = null;
this._hostParent = null;
this._hostContainerInfo = null;
this._domID = 0;
};

那么可以设想一下?什么情况下,node值会是null 或者 false?

回顾一下之前提到的jsx到js的转换,我们都jsx在转换后,children会依次按层级放在其父元素的type属性上。那么可想而知我们最终节点是没有children的,此时node值可能会是null 或者 false。

但是,虽然上文的推论很浅显直观,但是它是有漏洞的。不仅仅是自定义组件内部会含有html标记,而且一般来说末节点才有可能是null或者false,但是一般来说它都是string,也就是说末节点一般是常规的html标记。

准确的来讲,以下情况才是会发生返回这种children的场景:

1
2
3
4
5
6
7
class Foo extends React.Component{
render() {
// return
return null
}
}
ReactDom.render(<Foo />, container)

ReactCompositeComponent line511-514,对child的取值。当达到末节点,此时type为null。

1
2
3
4
var child = this._instantiateReactComponent(
renderedElement,
nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */,
);

而且这个实例中有一个属性 this._currentElement = null会被其用到。

ReactDOMComponent

ReactDOMComponent的情况可能是最容易理解的,当渲染的元素是HTML标准的divspan标记这类, 此时type类型就是string。

这种情况情况常常发生在末节点,和自定义组件里面的children里。

element.type(element)

暂时可用于一些非字符串标识的自定义组件,不过目前来说这是一个当前版本的妥协(临时措施), 未来版本会将其作为字符串标识,完成之后这个分支会被删除。

ReactCompositeComponentWrapper

ReactCompositeComponentWrapper比较常规。它用来处理自定义组件,比如之前提到的Foo组件就需要用它来处理一遍。

这个函数的作用呢就是对自定义组件做一些处理,比如我们的生命周期函数,事件绑定等一般都是在这里,当组件挂载、更新、卸载也都需要一个比较专门的逻辑来处理。不过虽然如此,但是这个函数其实也是相对抽象的,因为一旦到了type为string时候它会将事情转交给ReactDOMComponent模块。

它本身更多的是对自定义组件的生命周期调用、事务处理、状态更新进行了抽象。