Understanding The React Source Code — Initial Rendering (Simple Component) I

By Holmes He

Photo by Gerrie van der Walt on Unsplash
同学,本文也有中文版哦:)

Understanding The React Source Code I (this one)
Understanding The React Source Code II
Understanding The React Source Code III
Understanding The React Source Code IV
Understanding The React Source Code V
Understanding The React Source Code VI
Understanding The React Source Code VII
Understanding The React Source Code VIII
Understanding The React Source Code IX

UI updating, in its essential, is data change. React offers a straightforward and intuitive way to program front-end Apps with all the moving parts converged in the form of states. Code review is also made easier to me as I like to start with data structures for a rough expectation of the functionalities and processing logic. From time to time, I was curious about how React works internally, hence this article.

It never hurts to have a deeper understanding down the stack, as it gives me more freedom when I need a new feature, more confidence when I want to contribute and more comfort when I upgrade.

This article will start walking through one of the critical paths of React by rendering a simple component, i.e., a <h1>. Other topics (e.g., Composite components rendering, state driven UI updating and components life cycle) will be discussed in the following articles in the similar actionable manner.

Files used in this article:

isomorphic/React.js: entry point of ReactElement.createElement()

isomorphic/classic/element/ReactElement.js: workhorse of ReactElement.createElement()

renderers/dom/ReactDOM.js: entry point of ReactDOM.render()

renderers/dom/client/ReactMount.js: workhorse of ReactDom.render()

renderers/shared/stack/reconciler/instantiateReactComponent.js: create different types of ReactComponents based on element type

renderers/shared/stack/reconciler/ReactCompositeComponent.js: ReactComponents wrapper of root element

Tags used in the call stack:
- function call
= alias
~ indirect function call

As the locations of source code files can not be obviously derived from import statement in the flat module tree, I will use @ to help locating them in the code snippet listings.

Last words, this series is based on React 15.6.2.

I was not consciously aware of using React.createElement(), as the existence of it is masked by JSX from a developer’s point of view.

In compiling time, components defined in JSX is translated by Babel to React.createElement() called with appropriate parameters. For instance, the default App.js shipped with create-react-app:

import React, { Component } from ‘react’;
import logo from ‘./logo.svg’;
import ‘./App.css’;
class App extends Component {
render() {
return (
<div className=”App”>
<header className=”App-header”>
<img src={logo} className=”App-logo” alt=”logo” />
<h1 className=”App-title”>Welcome to React</h1>
</header>
<p className=”App-intro”>
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}
export default App;

is compiled to:

import React, { Component } from ‘react’;
import logo from ‘./logo.svg’;
import ‘./App.css’;
class App extends Component {
render() {
return React.createElement(
‘div’,
{ className: ‘App’ },
React.createElement(
‘header’,
{ className: ‘App-header’ },
React.createElement(‘img’, { src: logo, className: ‘App-logo’, alt: ‘logo’ }),
React.createElement(
‘h1’,
{ className: ‘App-title’ },
‘Welcome to React’
)
),
React.createElement(
‘p’,
{ className: ‘App-intro’ },
‘To get started, edit ‘,
React.createElement(
‘code’,
null,
‘src/App.js’
),
‘ and save to reload.’
)
);
}
}
export default App;

which is the real code executed by a browser. The code above shows a definition of a composite component App, in which JSX, a syntax of interweaving HTML tag in JavaScript code (e.g., <div className=”App”></div>), is translated to React.createElement() calls.

On the application level this component will be rendered:

ReactDOM.render(
<App />,
document.getElementById(‘root’)
);

normally by a JS entry module named “index.js”.

This nested components tree is a bit too complicated to be an ideal start point, so we forget it now and instead look at something easier - that renders a simple HTML element.


ReactDOM.render(
<h1 style={{“color”:”blue”}}>hello world</h1>,
document.getElementById(‘root’)
);

the babeled version of the above code is:


ReactDOM.render(React.createElement(
‘h1’,
{ style: { “color”: “blue” } },
‘hello world’
), document.getElementById(‘root’));

The first step does not do much really. It simply constructs an ReactElement instance populated with whatever passed down to the call stack. The outcome data structure is:

The call stack of this step is:

React.createElement
|=ReactElement.createElement(type, config, children)
|-ReactElement(type,…, props)

1. React.createElement(type, config, children) is merely an alias of ReactElement.createElement();


var createElement = ReactElement.createElement;

var React = {

createElement: createElement,

};
module.exports = React;
React@isomorphic/React.js

2. ReactElement.createElement(type, config, children) 1) copies the elements in config to props, 2) copies the children to props.children and 3) copies the type.defaultProps to props;


// 1)
if (config != null) {
…extracting not interesting properties from config…
// Remaining properties are added to a new props object
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
  // 2)
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
var childrenLength = arguments.length — 2;
if (childrenLength === 1) {
props.children = children; // scr: one child is stored as object
} else if (childrenLength > 1) {
var childArray = Array(childrenLength);
for (var i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2]; // scr: multiple children are stored as array
}
    props.children = childArray;
}
  // 3)
// Resolve default props
if (type && type.defaultProps) {
var defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
  return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
ReactElement.createElement@isomorphic/classic/element/ReactElement.js

3. Then ReactElement(type,…, props) copies the type and props as they are to ReactElement and returns the instance.


var ReactElement = function(type, key, ref, self, source, owner, props) {
// This tag allow us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: // scr: --------------> ‘h1’
key: // scr: --------------> not of interest for now
ref: // scr: --------------> not of interest for now
props: {
children: // scr: --------------> ‘hello world’
…other props: // scr: --------------> style: { “color”: “blue” }
},
// Record the component responsible for creating this element.
_owner: // scr: --------------> null
};
ReactElement@isomorphic/classic/element/ReactElement.js

The fields populated in the newly constructed ReactElement will be used directly by ReactMount.instantiateReactComponent(), which will be explained in detail very soon. Note that the next step will also create a ReactElement object with ReactElement.createElement(), so I will call the ReactElement object of this phase ReactElement[1].

The purpose of the next step is to wrap the ReactElement[1] with another ReactElement (we call the instance a [2]) and set the ReactElement.type with TopLevelWrapper. The name TopLevelWrapper explains what it does — wrap the top level element (of the component tree passed through render()):

An important definition here is that of TopLevelWrapper, I type three stars here *** for you to CTL-f, as you might need to come back to this definition later:


var TopLevelWrapper = function() {
this.rootID = topLevelRootCounter++;
};
TopLevelWrapper.prototype.isReactComponent = {};
TopLevelWrapper.prototype.render = function() {
// scr: this function will be used to strip the wrapper later in the // rendering process
  return this.props.child;
};
TopLevelWrapper.isReactTopLevelWrapper = true;
TopLevelWrapper@renderers/dom/client/ReactMount.js

Please note that the entity assigned to ReactElement.type is a type (TopLevelWrapper) which will be instantiated in the following rendering steps. Then ReactElement[1] will be extracted from this.props.child.

The call stack to construct the designated object is as following:

ReactDOM.render
|=ReactMount.render(nextElement, container, callback)
|=ReactMount._renderSubtreeIntoContainer(
parentComponent, // scr: --------------> null
nextElement, // scr: --------------> ReactElement[1]
container,// scr: --------------> document.getElementById(‘root’)
callback’ // scr: --------------> undefined
)

For initial rendering, ReactMount._renderSubtreeIntoContainer() is simpler than it seems to be, in fact, most branches (for UI updating) in this function are skipped. The only line that is effective before the logic processes to next step is


var nextWrappedElement = React.createElement(TopLevelWrapper, {
child: nextElement,
});
_renderSubtreeIntoContainer@renderers/dom/client/ReactMount.js

Now that it should be easy to see how the target object of this step is constructed with React.createElement, the function we just examined in the last section.

The purpose of this is step is to create a primitive ReactCompositeComponent for the top level component:

The call stack is:

ReactDOM.render
|=ReactMount.render(nextElement, container, callback)
|=ReactMount._renderSubtreeIntoContainer()
|-ReactMount._renderNewRootComponent(
nextWrappedElement, // scr: ------> ReactElement[2]
container, // scr: ------> document.getElementById(‘root’)
shouldReuseMarkup, // scr: null from ReactDom.render()
nextContext, // scr: emptyObject from ReactDom.render()
)
|-instantiateReactComponent(
node, // scr: ------> ReactElement[2]
shouldHaveDebugID /* false */
)
|-ReactCompositeComponentWrapper(
element // scr: ------> ReactElement[2]
);
|=ReactCompositeComponent.construct(element)

instantiateReactComponent is the only long function that is worth discussing here. In our context, it checks the ReactElement[2]’s type (i.e., TopLevelWrapper) and creates a ReactCompositeComponent accordingly.

function instantiateReactComponent(node, shouldHaveDebugID) {
var instance;

} else if (typeof node === ‘object’) {
var element = node;
var type = element.type;
    // Special case string values
if (typeof element.type === ‘string’) {

} else if (isInternalComponentType(element.type)) {

} else {
instance = new ReactCompositeComponentWrapper(element);
}
} else if (typeof node === ‘string’ || typeof node === ‘number’) {

} else {

}

return instance;
}
instantiateReactComponent@renderers/shared/stack/reconciler/instantiateReactComponent.js

It is worth noting that new ReactCompositeComponentWrapper() is a direct call of ReactCompositeComponent constructor:


// To avoid a cyclic dependency, we create the final class in this module
var ReactCompositeComponentWrapper = function(element) {
this.construct(element);
};

Object.assign(
ReactCompositeComponentWrapper.prototype,
ReactCompositeComponent,
{
_instantiateReactComponent: instantiateReactComponent,
},
);
ReactCompositeComponentWrapper@renderers/shared/stack/reconciler/instantiateReactComponent.js

Then the real constructor get called:

construct: function(element /* scr: ------> ReactElement[2] */) {
this._currentElement = element;
this._rootNodeID = 0;
this._compositeType = null;
this._instance = null;
this._hostParent = null;
this._hostContainerInfo = null;
// See ReactUpdateQueue
this._updateBatchNumber = null;
this._pendingElement = null;
this._pendingStateQueue = null;
this._pendingReplaceState = false;
this._pendingForceUpdate = false;
this._renderedNodeType = null;
this._renderedComponent = null;
this._context = null;
this._mountOrder = 0;
this._topLevelWrapper = null;
// See ReactUpdates and ReactUpdateQueue.
this._pendingCallbacks = null;
// ComponentWillUnmount shall only be called once
this._calledComponentWillUnmount = false;
},
ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js

We name the object of this step ReactCompositeComponent[T] (T for top).

After the ReactCompositeComponent object is constructed, the next step is to call batchedMountComponentIntoNode, initialize ReactCompositeComponent[T] and mount it, which will be discussed in detail in the next article.

I hope you like this read. If so, please clap for it or follow me on Medium. Thanks, and I hope to see you the next time.

Originally published at holmeshe.me.


Page 2

Photo by Jez Timms on Unsplash

同学,本文也有中文版哦:)

Understanding The React Source Code I
Understanding The React Source Code II (this one)
Understanding The React Source Code III
Understanding The React Source Code IV
Understanding The React Source Code V
Understanding The React Source Code VI
Understanding The React Source Code VII
Understanding The React Source Code VIII
Understanding The React Source Code IX

In the last article we went through the process that creates a ReactCompositeComponent instance from JSX expression. This one will continue the walk-through of simple component rendering, from the point of batchedMountComponentIntoNode().

Files used in this article:
renderers/dom/client/ReactMount.js: defines mountComponentIntoNode(), entry point of the logic process in this article, and ReactDOMContainerInfo which is used in DOM rendering

renderers/shared/stack/reconciler/ReactReconciler.js: call mountComponent of various ReactXXXComponent

renderers/shared/stack/reconciler/ReactCompositeComponent.js: call mountComponent to instantiate TopLevelWrapper, and performInitialMount to instantiate ReactDOMComponent

renderers/dom/shared/ReactDOMComponent.js: define ReactDOMComponent

A complete static call hierarchy used in this article:

|=ReactMount.render(nextElement, container, callback)     ___
|=ReactMount._renderSubtreeIntoContainer() |
|-ReactMount._renderNewRootComponent() |
|-instantiateReactComponent() |
|~batchedMountComponentIntoNode() upper half
|~mountComponentIntoNode() (platform agnostic)
|-ReactReconciler.mountComponent() |
|-ReactCompositeComponent.mountComponent() |
|-ReactCompositeComponent.performInitialMount() |
|-instantiateReactComponent() _|_
|-ReactDOMComponent.mountComponent() lower half
|-_mountImageIntoNode() (HTML DOM specific)
_|_

batchedMountComponentIntoNode() does not do much. It simply invokes another function call to mountComponentIntoNode().

For now let’s omit the delicacy of those indirect function calls and just see them as direct ones. I will cover transaction and batched updates in later articles.

mountComponentIntoNode() is the cross point of logic that consists of platform agnostic code (in this article referred as “upper half”) and logic that is HTML specific (“lower half”). All major tasks of this walk-through are also effectively completed within this function (and its sub-calls), from where 1) a ReactDOMComponent is derived from the top level ReactCompositeComponent[T]; 2) the ReactDOMComponent is rendered to a real DOM element, and 3) the element is inserted into the document object.

function mountComponentIntoNode(
wrapperInstance, // scr: -----> ReactCompositeComponent[T]
container, // scr: -----> document.getElementById(‘root’)
transaction, // scr: -----> not of interest
shouldReuseMarkup, // scr: -----> null
context, // scr: -----> emptyObject
) {
...
var markup = ReactReconciler.mountComponent( // scr: -----> 1),2)
wrapperInstance,
transaction,
null,
ReactDOMContainerInfo(wrapperInstance, container),
context,
0 /* parentDebugID */,
);
...
  ReactMount._mountImageIntoNode(              // scr: -----> 3)
markup,
container,
wrapperInstance,
shouldReuseMarkup,
transaction,
);
ReactMount@renderers/dom/client/ReactMount.js

In which, 1) is still conducted as a high level operation in upper half, while 2), 3) are concrete DOM operations. After 2) completes, we will be able to see the element <h1 style={{“color”:”blue”}}>hello world</h1> rendered on the screen.

For 1), ReactReconciler.mountComponent() is yet another simple function that calls the corresponding mountComponent() of internalInstance passed to it, in this case, ReactCompositeComponent[T].

mountComponent: function(
internalInstance,
transaction,
hostParent,
hostContainerInfo,
context,
parentDebugID, // 0 in production and for roots
) {
var markup = internalInstance.mountComponent(
transaction,
hostParent,
hostContainerInfo,
context,
parentDebugID,
);
... // scr: transaction related code
return markup;
},
ReactReconciler@renderers/shared/stack/reconciler/ReactReconciler.js

One special parameter here is ReactDOMContainerInfo, it is constructed at the same time when it is passed down to ReactReconciler.mountComponent():

function ReactDOMContainerInfo(topLevelWrapper, node) {
var info = {
_topLevelWrapper: topLevelWrapper, // scr: -------------------> ReactCompositeComponent[T]
_idCounter: 1,
_ownerDocument: node ? node.nodeType === DOC_NODE_TYPE ? node : node.ownerDocument : null, // scr: -----> node.nowerDocument
_node: node, // src: -----> document.getElementById(‘root’)
_tag: node ? node.nodeName.toLowerCase() : null, // scr: -----> 'div'
_namespaceURI: node ? node.namespaceURI : null // scr: ----->
element.namespaceURI
};
... // scr: DEV code
return info;
}
ReactDOMContainerInfo@renderers/dom/client/ReactMount.js

The result object of this constructor is ReactDOMContainerInfo[ins] which will be used by 3).

Here is the end of the corridor that consists of transient functions. Now we move on to the next important stop.

This is where the magic, I mean, the reaction happens

In the previous step only _currentElement of ReactCompositeComponent[T] is populated with a reference to ReactElement[2], which makes the object a little bit dull. But not anymore. This property will in turn be used to trigger a “reaction” within ReactCompositeComponent[T] and transforms (initialize) the object to something more meaningful.

The designated data structure of this step is:

The call stack in action is:

ReactDOM.render
|=ReactMount.render(nextElement, container, callback)
|=ReactMount._renderSubtreeIntoContainer()
|-ReactMount._renderNewRootComponent()
|-instantiateReactComponent()
|~batchedMountComponentIntoNode(
componentInstance, // scr: -----> ReactCompositeComponent[T]
container, // scr: -> document.getElementById(‘root’)
shouldReuseMarkup, // scr: -----> null
context, // scr: -----> emptyObject
)
|~mountComponentIntoNode(
wrapperInstance, // scr: -----> ReactCompositeComponent[T]
container, // scr: -----> same
transaction, // scr: -----> not of interest
shouldReuseMarkup, // scr: ---> same
context, // scr: -----> not of interest
)
|-ReactReconciler.mountComponent(
internalInstance, // scr: --> ReactCompositeComponent[T]
transaction, // scr: --> not of interest
hostParent, // scr: --> null
hostContainerInfo,// scr: --> ReactDOMContainerInfo[ins]
context, // scr: --> not of interest
parentDebugID, // scr: --> 0
)
/* we are here */
|-ReactCompositeComponent[T].mountComponent(same)

Next let’s look at the ReactCompositeComponent.mountComponent() implementation.

I will not show the implementation for small functions (when they are get called) but give return value directly for the sake of concision.
mountComponent: function(
transaction,
hostParent,
hostContainerInfo,
context,
// scr: this ------> ReactCompositeComponent[T]
) {
// scr: --------------------------------------------------------> 1)
this._context = context; // scr: -----> emptyObject
this._mountOrder = nextMountID++; // scr: ----------------------> global veriable, accumulative
this._hostParent = hostParent; // scr: -----> null
this._hostContainerInfo = hostContainerInfo; // scr: -----------> ReactDOMContainerInfo[ins]
  var publicProps = this._currentElement.props; // scr: ----------> { child: ReactElement[1] }
  var publicContext = this._processContext(context); // scr: -----> meaning less, emptyObject
// scr: --------------------------------------------------------> 2)
var Component = this._currentElement.type; // scr: -------------> TopLevelWrapper
  var updateQueue = transaction.getUpdateQueue(); // scr: --------> not of interest
// Initialize the public class
var doConstruct = shouldConstruct(Component); // scr: ----------> true, for TopLevelWrapper.prototype.isReactComponent = {};
  var inst = this._constructComponent(
doConstruct,
publicProps,
publicContext,
updateQueue,
); // scr: ----------> call TopLevelWrapper’s constructor
  var renderedElement;
// Support functional components
if (!doConstruct && (inst == null || inst.render == null)) {

} else {
if (isPureComponent(Component)) { // scr: --------------------> TopLevelWrapper.prototype.isPureReactComponent is not defined

} else {
this._compositeType = CompositeTypes.ImpureClass;
}
}
// scr: --------------------------------------------------------> 3)
// These should be set up in the constructor, but as a convenience
// for simpler class abstractions, we set them up after the fact.
inst.props = publicProps; // scr: ----> { child: ReactElement[1] }
// scr: --------------------------------------------------------> 4)
this._instance = inst; // scr: ---------------------------------> link the ReactCompositeComponent[T] to the TopLevelWrapper instance
// Store a reference from the instance back to the internal representation
ReactInstanceMap.set(inst, this); // scr: ----------------------> link the TopLevelWrapper instance back to ReactCompositeComponent[T]
  var markup;
if (inst.unstable_handleError) { // scr: -----------------------> false, TopLevelWrapper.prototype.unstable_handleError is not defined

} else {
// scr: --------------------------------------------------------> 5)
markup = this.performInitialMount( // scr: a initial at the end?
renderedElement,
hostParent,
hostContainerInfo,
transaction,
context,
);
}
  return markup;
}
ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js

Running in the context of ReactCompositeComponent[T] (as a member function), this method

1) assigns the parameters directly to the corresponding properties of ReactCompositeComponent[T] and local variables. The interesting one variable here is publicProps. It will be used very soon;

2) extracts TopLevelWrapper from this._currentElement.type, and calls its constructor to create a TopLevelWrapper instance. In this process:

shouldConstruct(Component)

checks if TopLevelWrapper.prototype.isReactComponent is set, and returns true if so; and

this._constructComponent()

calls the TopLevelWrapper constructor if true is the result of the previous function. I will name the instance TopLevelWrapper[ins] this time;

This is the time you may want to check the definition of TopLevelWrapper in the previous post, search for ***.

3) initializes the TopLevelWrapper[ins].props of the new instance with the information stored previously in the wrapper element ReactElement[2]through the publicProps in 1);

4) creates a doubly link between this (ReactCompositeComponent[T]) and TopLevelWrapper[ins]. One link is created using this._instance, another is made with ReactInstanceMap. this._instance will be used very soon in the next step, and ReactInstanceMap will be used in later posts;

I type 4 stars **** here as you might need to come back to check ReactInstanceMap‘s origin.

5) goes to the next step.

This step strips the wrapper and creates a ReactDOMComponent instance. Plus, we are going to see ReactHostComponent the first time. This component is used to chain the function call from the upper half to lower half.

First thing first, the target data structure:

The call stack in action:

ReactDOM.render
|=ReactMount.render(nextElement, container, callback)
|=ReactMount._renderSubtreeIntoContainer()
|-ReactMount._renderNewRootComponent()
|-instantiateReactComponent()
|~batchedMountComponentIntoNode()
|~mountComponentIntoNode()
|-ReactReconciler.mountComponent()
|-ReactCompositeComponent[T].mountComponent(same)
/* we are here */
|-ReactCompositeComponent[T].performInitialMount(
renderedElement, // scr: -------> undefined
hostParent, // scr: -------> null
hostContainerInfo, // scr: -------> ReactDOMContainerInfo[ins]
transaction, // scr: -------> not of interest
context, // scr: -------> not of interest
)

ReactCompositeComponent.performInitialMount() does three things. It 1) extracts the ReactElement[1] as mentioned before; 2) instantiates a ReactDOMComponent based on the ReactElement[1].type; and 3) calls ReactDOMComponent.mountComponent() to render a DOM element.

why a performInitialMount is invoked at the end of the mountComponent function? It is “initial” for the next mountComponent
performInitialMount: function(
renderedElement,
hostParent,
hostContainerInfo,
transaction,
context,
) {
var inst = this._instance;
  var debugID = 0;
  if (inst.componentWillMount) { // scr: ----------> undefined

}
  // scr: ------------------------------------------------------> 1)
// If not a stateless component, we now render
if (renderedElement === undefined) {
renderedElement = this._renderValidatedComponent(); // scr: ---> calls TopLevelWrapper.render() to extract ReactElement[1].
}
  // scr: ------------------------------------------------------> 2)
var nodeType = ReactNodeTypes.getType(renderedElement); // scr: -> ReactNodeTypes.HOST
  this._renderedNodeType = nodeType;
var child = this._instantiateReactComponent(
renderedElement,
nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */,
);
  this._renderedComponent = child;
  // scr: ------------------------------------------------------> 3)
var markup = ReactReconciler.mountComponent(
child,
transaction,
hostParent,
hostContainerInfo,
this._processChildContext(context),
debugID,
);
  return markup;
},
ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js

Now I give a detailed explanation for each step:

1) this._renderValidatedComponent() simply calls TopLevelWrapper.render() so that ReactElement[1] extracted from TopLevelWrapper[T].props.child is assigned to renderedElement.

2) this._instantiateReactComponent() is an alias to _instantiateReactComponent@renderers/shared/stack/reconciler/instantiateReactComponent.js that has been discussed in the last post. However, renderedElement(i.e., ReactElement[1]) is used to create a ReactDOMComponent instance this time…

wait here,

if we look at the implementation of _instantiateReactComponent() again

...
// Special case string values
if (typeof element.type === ‘string’) { // scr: -------> this time
instance = ReactHostComponent.createInternalComponent(element);
}
...
_instantiateReactComponent@renderers/shared/stack/reconciler/instantiateReactComponent.js

the createInternalComponent() of ReactHostComponent is got called because ReactElement[1].type is “h1”, which instantiate a genericComponentClass:

function createInternalComponent(element) {
...
return new genericComponentClass(element);
}
ReactHostComponent@renderers/shared/stack/reconciler/ReactHostComponent.js

but why it has anything to do with ReactDOMComponent?

In fact, the platform specific component ReactDOMComponent is “injected” to ReactHostComponent as genericComponentClass during compiling time. For now we consider link has already been established and injection mechanism will be discussed later posts.

I type *5 here.

The constructor of ReactDOMComponent is very similar to that of ReactCompositeComponent:

function ReactDOMComponent(element) {
var tag = element.type; // scr: --------> 'h1'
...
this._currentElement = element; // scr: --------> ReactElement[1]
this._tag = tag.toLowerCase(); // scr: --------> 'h1'
... // scr: default values, null, 0, etc.
}
ReactDOMComponent@renderers/dom/shared/ReactDOMComponent.js

We name the returned instance ReactDOMComponent[ins];

3) ReactReconciler.mountComponent()has already been covered, it simply calls the mountComponent() of the first parameter, in this case, ReactDOMComponent[ins].

Now the logic processes to the lower half.

to be continued…

I hope you like this read. If so, please clap for it or follow me on Medium. Thanks, and I hope to see you the next time.👋

Originally published at holmeshe.me.