What happened when we first called reactdom.render in the React project? Today I’ll trace the issue from a source perspective (focusing on the flow, not the details).
ReactDOMStackEntry
First we can find the Render method in the ReactDOM entry file, reactdomStackEntry.js. As you can see, the Render method is provided by the ReactMount component.
var ReactDOMStack = {
findDOMNode: findDOMNode,
render: ReactMount.render,
unmountComponentAtNode: ReactMount.unmountComponentAtNode,
version: ReactVersion,
/* eslint-disable camelcase */
unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
unstable_renderSubtreeIntoContainer: ReactMount.renderSubtreeIntoContainer,
/* eslint-enable camelcase */
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
// For TapEventPlugin which is popular in open source
EventPluginHub: require('EventPluginHub'),
// Used by test-utils
EventPluginRegistry: require('EventPluginRegistry'),
EventPropagators: require('EventPropagators'),
ReactControlledComponent: require('ReactControlledComponent'),
ReactDOMComponentTree,
ReactDOMEventListener: require('ReactDOMEventListener'),
ReactUpdates: ReactUpdates,
},
};Copy the code
ReactMount.render
render: function(nextElement, container, callback) {
return ReactMount._renderSubtreeIntoContainer(
null,
nextElement,
container,
callback,
);
},Copy the code
And transferred to _renderSubtreeIntoContainer method, this method is the core content is as follows:
ReactMount._renderSubtreeIntoContainer
_renderSubtreeIntoContainer: function( parentComponent, nextElement, container, callback, ) { callback = callback === undefined ? null : callback; if (! React.isValidElement(nextElement)) { ... Var nextWrappedElement = react. createElement(TopLevelWrapper, {child: nextElement,}); var nextContext = getContextForSubtree(parentComponent); / / to get at the top of the current container components var prevComponent = getTopLevelWrapperInContainer (container); PrevComponent null if (prevComponent) {var prevWrappedElement = prevComponent._currentelement; var prevElement = prevWrappedElement.props.child; if (shouldUpdateReactComponent(prevElement, nextElement)) { var publicInst = prevComponent._renderedComponent.getPublicInstance(); var updatedCallback = callback && function() { validateCallback(callback); callback.call(publicInst); }; ReactMount._updateRootComponent( prevComponent, nextWrappedElement, nextContext, container, updatedCallback, ); return publicInst; } else { ReactMount.unmountComponentAtNode(container); } } var reactRootElement = getReactRootElementInContainer(container); var containerHasReactMarkup = reactRootElement && !! internalGetID(reactRootElement); var containerHasNonRootReactChild = hasNonRootReactChild(container); var shouldReuseMarkup = containerHasReactMarkup && ! prevComponent && ! containerHasNonRootReactChild; var component = ReactMount._renderNewRootComponent( nextWrappedElement, container, shouldReuseMarkup, nextContext, callback, )._renderedComponent.getPublicInstance(); return component; },Copy the code
Here are a few methods:
getTopLevelWrapperInContainer
shouldUpdateReactComponent
_renderNewRootComponent
getTopLevelWrapperInContainer
This method is used to retrieve the existing top-level container component, as shown below.
function getTopLevelWrapperInContainer(container) { var root = getHostRootInstanceInContainer(container); return root ? root._hostContainerInfo._topLevelWrapper : null; } function getHostRootInstanceInContainer(container) { var rootEl = getReactRootElementInContainer(container); var prevHostInstance = rootEl && ReactDOMComponentTree.getInstanceFromNode(rootEl); return prevHostInstance && ! prevHostInstance._hostParent ? prevHostInstance : null; }Copy the code
Call the two methods, getReactRootElementInContainer and ReactDOMComponentTree getInstanceFromNode.
getReactRootElementInContainer
function getReactRootElementInContainer(container) {
if (!container) {
return null;
}
if (container.nodeType === DOCUMENT_NODE) {
return container.documentElement;
} else {
return container.firstChild;
}
}Copy the code
ReactDOMComponentTree
This module has three methods, which are:
precacheChildNodes
Store the React instance on the DOM nodegetNodeFromInstance
Get the corresponding DOM node from an instancegetInstanceFromNode
Get the corresponding instance from a DOM node
shouldUpdateReactComponent
Determine whether the component needs to be updated.
function shouldUpdateReactComponent(prevElement, nextElement) { var prevEmpty = prevElement === null || prevElement === false; var nextEmpty = nextElement === null || nextElement === false; if (prevEmpty || nextEmpty) { return prevEmpty === nextEmpty; } var prevType = typeof prevElement; var nextType = typeof nextElement; if (prevType === 'string' || prevType === 'number') { return nextType === 'string' || nextType === 'number'; } else { return ( nextType === 'object' && prevElement.type === nextElement.type && prevElement.key === nextElement.key ); }}Copy the code
You can see that the logic looks like this:
- Both elements are zero
null
returntrue
- If it is
textComponent
, then directly update - Otherwise, if the element is a DOM element or React element, and the type and key are the same
true
, the implementation of the update
ReactMount._renderNewRootComponent
This method is the core of _renderSubtreeIntoContainer, used to mount a new component to the DOM.
_renderNewRootComponent: function( nextElement, container, shouldReuseMarkup, context, callback, Through instantiateReactComponent) {/ / get the React Component Component instance var componentInstance = instantiateReactComponent(nextElement, false); if (callback) { componentInstance._pendingCallbacks = [ function() { validateCallback(callback); callback.call( componentInstance._renderedComponent.getPublicInstance(), ); }, ]; } // The initial render is synchronous but any updates that happen during // rendering, in componentWillMount or componentDidMount, will be batched // according to the current batching strategy. ReactUpdates.batchedUpdates( batchedMountComponentIntoNode, componentInstance, container, shouldReuseMarkup, context, ); var wrapperID = componentInstance._instance.rootID; instancesByReactRootID[wrapperID] = componentInstance; return componentInstance; },Copy the code
instantiateReactComponent
Generate different React Components based on the parameters passed in.
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') { ... } // Special case string values if (typeof element.type === 'string') { instance = ReactHostComponent.createInternalComponent(element); } else if (isInternalComponentType(element.type)) { // This is temporarily available for custom components that are not string // representations. I.e. ART. Once those are updated to use the string // representation, we can drop this code path. instance = new element.type(element); // We renamed this. Allow the old name for compat. :( 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 { invariant(false, 'Encountered invalid React node of type %s', typeof node); }Copy the code
There are three methods to generate three React component instances, depending on the element. Type:
- ReactHostComponent.createInternalComponent(element)
- new ReactCompositeComponentWrapper(element)
- ReactHostComponent.createInstanceForText(node);
If isInternalComponentType(element.type) is set, instance = new Element.type (element); if isInternalComponentType(element.type) is set, instance = new Element.type (element); This code is ignored by me because it is the solution when React encapsulates internal components that are not expressed by strings, so we don’t have to worry about it. Let’s look at the three methods above, two of which call the ReactHostComponent module.
ReactHostComponent
Core code:
var ReactHostComponentInjection = { // This accepts a class that receives the tag string. This is a catch all // that can render any kind of tag. injectGenericComponentClass: function(componentClass) { genericComponentClass = componentClass; }, // This accepts a text component class that takes the text string to be // rendered as props. injectTextComponentClass: function(componentClass) { textComponentClass = componentClass; }}; function createInternalComponent(element) { invariant( genericComponentClass, 'There is no registered component for the tag %s', element.type, ); return new genericComponentClass(element); } /** * @param {ReactText} text * @return {ReactComponent} */ function createInstanceForText(text) { return new textComponentClass(text); }Copy the code
Two methods are provided to create components, and the implementation of two component classes is injected from other modules. After a search, found to be in ReactDOMStackInjection. Injected in js, we look at the code:
var ReactComponentEnvironment = require('ReactComponentEnvironment');
var ReactComponentBrowserEnvironment = require('ReactComponentBrowserEnvironment');
var ReactDOMComponent = require('ReactDOMComponent');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactDOMEmptyComponent = require('ReactDOMEmptyComponent');
var ReactDOMTextComponent = require('ReactDOMTextComponent');
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
var ReactEmptyComponent = require('ReactEmptyComponent');
var ReactGenericBatching = require('ReactGenericBatching');
var ReactHostComponent = require('ReactHostComponent');
var ReactReconcileTransaction = require('ReactReconcileTransaction');
var ReactUpdates = require('ReactUpdates');
var findDOMNode = require('findDOMNode');
var getHostComponentFromComposite = require('getHostComponentFromComposite');
ReactGenericBatching.injection.injectStackBatchedUpdates(
ReactUpdates.batchedUpdates,
);
ReactHostComponent.injection.injectGenericComponentClass(ReactDOMComponent);
ReactHostComponent.injection.injectTextComponentClass(ReactDOMTextComponent);
ReactEmptyComponent.injection.injectEmptyComponentFactory(function(
instantiate,
) {
return new ReactDOMEmptyComponent(instantiate);
});
ReactUpdates.injection.injectReconcileTransaction(ReactReconcileTransaction);
ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);
ReactComponentEnvironment.injection.injectEnvironment(
ReactComponentBrowserEnvironment,
);
findDOMNode._injectStack(function(inst) {
inst = getHostComponentFromComposite(inst);
return inst ? ReactDOMComponentTree.getNodeFromInstance(inst) : null;
});Copy the code
You can see, this module by ReactHostComponent. Injection injected ReactDOMComponent and ReactDOMTextComponent. Some other modules are also injected, which we’ll use later.
Here the ReactDOMComponent and ReactDOMTextComponent are the real DOM tag generating modules. They are too much content, but the code is relatively simple and I won’t go into details here. Calling the mountComponent method of both modules generates DOM Markup. The difference is that the ReactDOMComponent will have the Markup of the following structure
{
node: node,
children: [],
html: null,
text: null,
toString
}Copy the code
The ReactDOMTextComponent directly generates the String text to render in the DOM.
ReactCompositeComponent
The last class of components should be ReactCompositeComponentWrapper, but see instantiateReactComponent inside the code:
var ReactCompositeComponentWrapper = function(element) {
this.construct(element);
};
Object.assign(
ReactCompositeComponentWrapper.prototype,
ReactCompositeComponent,
{
_instantiateReactComponent: instantiateReactComponent,
},
);Copy the code
When this.construct is called, it also calls the ReactCompositeComponent, which is the user-defined component.
ReactUpdates.batchedUpdates
function batchedUpdates(callback, a, b, c, d, e) {
ensureInjected();
return batchingStrategy.batchedUpdates(callback, a, b, c, d, e);
}Copy the code
ReactUpdates call batchedUpdate with batchingStrategy. The batchingStrategy is also injected with the ReactDOMStackInjection described earlier.
ReactUpdates.injection.injectReconcileTransaction(ReactReconcileTransaction);
ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);Copy the code
ReactUpdates were injected into the two modules, respectively is ReactReconcileTransaction and ReactDefaultBatchingStrategy.
To look at the first ReactDefaultBatchingStrategy, behind ReactReconcileTransaction met again, take a look at its code:
var ReactUpdates = require('ReactUpdates'); var Transaction = require('Transaction'); var emptyFunction = require('fbjs/lib/emptyFunction'); var RESET_BATCHED_UPDATES = { initialize: emptyFunction, close: function() { ReactDefaultBatchingStrategy.isBatchingUpdates = false; }}; var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction, close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates), }; var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES]; function ReactDefaultBatchingStrategyTransaction() { this.reinitializeTransaction(); } Object.assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction, { getTransactionWrappers: function() { return TRANSACTION_WRAPPERS; }}); var transaction = new ReactDefaultBatchingStrategyTransaction(); var ReactDefaultBatchingStrategy = { isBatchingUpdates: false, /** * Call the provided function in a context within which calls to `setState` * and friends are batched such that components aren't updated unnecessarily. */ batchedUpdates: function(callback, a, b, c, d, e) { var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; ReactDefaultBatchingStrategy.isBatchingUpdates = true; // The code is written this way to avoid extra allocations if (alreadyBatchingUpdates) {// If current updates were completed, Callback callback(a, b, c, d, e); Perform (callback, null, a, b, c, d, e); }}};Copy the code
. You can see our previous call ReactUpdates batchUpdates batchedUpdates actually calls are here, the inside of the logic is simple.
There is a transaction method that is used. That is, if an update is currently in progress, the callback is called transactionally.
transaction
Transaction is used extensively in the React source code to call a method transactionally.
Wrap a method with one or more Wrappers and execute it before and after the method call. The transaction ensures that the Initialize and close methods of the Wrapper will execute, regardless of whether the intended method succeeds or fails.
* <pre>
* wrappers (injected at creation time)
* + +
* | |
* +-----------------|--------|--------------+
* | v | |
* | +---------------+ | |
* | +--| wrapper1 |---|----+ |
* | | +---------------+ v | |
* | | +-------------+ | |
* | | +----| wrapper2 |--------+ |
* | | | +-------------+ | | |
* | | | | | |
* | v v v v | wrapper
* | +---+ +---+ +---------+ +---+ +---+ | invariants
* perform(anyMethod) | | | | | | | | | | | | maintained
* +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | +---+ +---+ +---------+ +---+ +---+ |
* | initialize close |
* +-----------------------------------------+
* </pre>Copy the code
Here we go back to just ReactDefaultBatchingStrategy, there are two wrapper code.
var RESET_BATCHED_UPDATES = { initialize: emptyFunction, close: function() { ReactDefaultBatchingStrategy.isBatchingUpdates = false; }}; var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction, close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates), };Copy the code
The initialize method is an empty function, and the close method is:
- Set the current update status to false
flushBatchedUpdates
This method is a bit more complicated and won’t be covered here. The main thing is to ensure that all components are updated correctly (flushBatchedUpdates->ReactUpdates.runBatchedUpdates->ReactCompositeComponent.performUpdateIfNecessary
)
Now back to the code above:
ReactUpdates.batchedUpdates(
batchedMountComponentIntoNode,
componentInstance,
container,
shouldReuseMarkup,
context,
);Copy the code
Can know, this is called the batchedMountComponentIntoNode for subsequent work.
function batchedMountComponentIntoNode( componentInstance, container, shouldReuseMarkup, context, ) { var transaction = ReactUpdates.ReactReconcileTransaction.getPooled( /* useCreateElement */ ! shouldReuseMarkup, ); transaction.perform( mountComponentIntoNode, null, componentInstance, container, transaction, shouldReuseMarkup, context, ); ReactUpdates.ReactReconcileTransaction.release(transaction); }Copy the code
Here the first assignment will use the top into the inside of the ReactUpdates another module ReactReconcileTransaction React is scheduling transaction module.
var SELECTION_RESTORATION = { initialize: ReactInputSelection.getSelectionInformation, close: ReactInputSelection.restoreSelection, }; /** * Suppresses events (blur/focus) that could be inadvertently dispatched due to * high level DOM manipulations (like temporarily removing a text input from the * DOM). */ var EVENT_SUPPRESSION = { initialize: function() { var currentlyEnabled = ReactBrowserEventEmitter.isEnabled(); ReactBrowserEventEmitter.setEnabled(false); return currentlyEnabled; }, close: function(previouslyEnabled) { ReactBrowserEventEmitter.setEnabled(previouslyEnabled); }}; /** * Provides a queue for collecting `componentDidMount` and * `componentDidUpdate` callbacks during the transaction. */ var ON_DOM_READY_QUEUEING = { initialize: function() { this.reactMountReady.reset(); }, close: function() { this.reactMountReady.notifyAll(); }}; . var TRANSACTION_WRAPPERS = [ SELECTION_RESTORATION, EVENT_SUPPRESSION, ON_DOM_READY_QUEUEING, ]; function ReactReconcileTransaction(useCreateElement) { this.reinitializeTransaction(); this.renderToStaticMarkup = false; this.reactMountReady = CallbackQueue.getPooled(); this.useCreateElement = useCreateElement; }... var Mixin = { getTransactionWrappers: function() { return TRANSACTION_WRAPPERS; },... } Object.assign(ReactReconcileTransaction.prototype, Transaction, Mixin); PooledClass.addPoolingTo(ReactReconcileTransaction);Copy the code
This is also done transactionally, with three wrappers:
- Selection Restoration minimizes user Selection range during updates
- Event Suppression Suppression of unnecessary Event distribution, such as blur events caused by the temporary deletion of an input element
- On DOM Ready Queueing is provided during transaction execution
componentDidMount
和componentDidUpdate
A queue of callback functions
The getPooled method, which follows, is a way to use instance pools to avoid unnecessary GC.
Then use this transaction to call mountComponentIntoNode. Take a look at this method in detail.
mountComponentIntoNode
function mountComponentIntoNode(
wrapperInstance,
container,
transaction,
shouldReuseMarkup,
context,
) {
var markup = ReactReconciler.mountComponent(
wrapperInstance,
transaction,
null,
ReactDOMContainerInfo(wrapperInstance, container),
context,
0 /* parentDebugID */,
);
wrapperInstance._renderedComponent._topLevelWrapper = wrapperInstance;
ReactMount._mountImageIntoNode(
markup,
container,
wrapperInstance,
shouldReuseMarkup,
transaction,
);
}Copy the code
Here you see operations on the key markup variable, which is the object we will eventually render into the DOM. Through ReactReconciler. MountComponent method to get the markup. ReactReconciler. MountComponent source code is as follows:
mountComponent: function( internalInstance, transaction, hostParent, hostContainerInfo, context, parentDebugID, // 0 in production and for roots ) { var markup = internalInstance.mountComponent( transaction, hostParent, hostContainerInfo, context, parentDebugID, ); if ( internalInstance._currentElement && internalInstance._currentElement.ref ! = null ) { transaction.getReactMountReady().enqueue(attachRefs, internalInstance); } return markup; },Copy the code
It calls the internalInstance mountComponent, internalInstance here is actually said is obtained by instantiateReactComponent React in front of the Component instance.
// _renderNewRootComponent
var componentInstance = instantiateReactComponent(nextElement, false)Copy the code
Here nextElement is the React root element to render.
// _renderSubtreeIntoContainer
var nextWrappedElement = React.createElement(TopLevelWrapper, {
child: nextElement,
});Copy the code
The implementation of TopLevelWrapper needs to pay attention to its render method.
var TopLevelWrapper = function() {
this.rootID = topLevelRootCounter++;
};
TopLevelWrapper.prototype.isReactComponent = {};
TopLevelWrapper.prototype.render = function() {
return this.props.child;
};
TopLevelWrapper.isReactTopLevelWrapper = true;Copy the code
This.props. Child = nextElement (
The inside of the back ReactReconciler. MountComponent internalInstance mountComponent. Return through said instantiateReactComponent we know in front of three components:
- ReactDOMComponent
- ReactDOMTextComponent
- ReactCompositeComponent
The first two classes are simple. They are elements of the DOM itself, and eventually render their corresponding Markup. ReactCompositeComponent is more complex, so let’s just look at the key code:
//ReactCompositeComponent.mountComponent var Component = this._currentElement.type; var updateQueue = transaction.getUpdateQueue(); // Initialize the public class var doConstruct = shouldConstruct(Component); var inst = this._constructComponent( doConstruct, publicProps, publicContext, updateQueue, ); var renderedElement; if (! doConstruct && (inst == null || inst.render == null)) { renderedElement = inst; inst = new StatelessComponent(Component); this._compositeType = ReactCompositeComponentTypes.StatelessFunctional; } else {. . } markup = this.performInitialMount( renderedElement, hostParent, hostContainerInfo, transaction, context, );Copy the code
First get inst, get inst the call stack is this: enclosing _constructComponent – > enclosing _constructComponentWithoutOwner
// this._constructComponentWithoutOwner ... var Component = this._currentElement.type; if (doConstruct) { if (__DEV__) { return measureLifeCyclePerf( () => new Component(publicProps, publicContext, updateQueue), this._debugID, 'ctor', ); } else { return new Component(publicProps, publicContext, updateQueue); }}...Copy the code
CurrentElement. Type is the class inherited from react.component.component.component.function. RenderElement is then declared, with renderElement = inst for stateless (pure render component declared by function) components, undefined otherwise.
And then performInitialMount,
if (renderedElement === undefined) { renderedElement = this._renderValidatedComponent(); } var nodeType = ReactNodeTypes.getType(renderedElement); this._renderedNodeType = nodeType; var child = this._instantiateReactComponent( renderedElement, nodeType ! == ReactNodeTypes.EMPTY /* shouldHaveDebugID */, ); this._renderedComponent = child; var markup = ReactReconciler.mountComponent( child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), debugID, );Copy the code
For non-Stateless components, renderedElement needs to be assigned. The call stack is: the enclosing _renderValidatedComponent – > enclosing _renderValidatedComponentWithoutOwnerOrContext
_renderValidatedComponentWithoutOwnerOrContext: function() {
var inst = this._instance;
var renderedElement;
if (__DEV__) {
renderedElement = measureLifeCyclePerf(
() => inst.render(),
this._debugID,
'render',
);
} else {
renderedElement = inst.render();
}
if (__DEV__) {
// We allow auto-mocks to proceed as if they're returning null.
if (renderedElement === undefined && inst.render._isMockFunction) {
// This is probably bad practice. Consider warning here and
// deprecating this convenience.
renderedElement = null;
}
}
return renderedElement;
},Copy the code
We end up with renderedElement after Inst.render ().
Then go down the child = this. _instantiateReactComponent (renderedElement) and markup = ReactReconciler. MountComponent (child,…). .
Can relate to, and here is the cycle of recursive calls ReactReconciler. MountComponent, know the child is not ReactCompositeComponent so far, The data structure for the final MarkUp. MarkUp can be found in DOMLazyTree:
// DOMLazyTree
{
node: node,
children: [],
html: null,
text: null,
toString,
}Copy the code
Once you have the MarkUp, the final step is to mount the MarkUp into the actual DOM via reactmount._mountimageintonode.
ReactMount._mountImageIntoNode
_mountImageIntoNode: function( markup, container, instance, shouldReuseMarkup, transaction, ) { invariant( isValidContainer(container), 'mountComponentIntoNode(...) : Target container is not valid.', ); If (shouldReuseMarkup) {if (shouldReuseMarkup) {if (shouldReuseMarkup) { } // First render, transaction.useCreateElement = true if (transaction.useCreateElement) { while (container.lastChild) { container.removeChild(container.lastChild); } DOMLazyTree.insertTreeBefore(container, markup, null); } else { setInnerHTML(container, markup); ReactDOMComponentTree.precacheNode(instance, container.firstChild); }},Copy the code
The logic is simple. There are two ways to render markup into the DOM:
- Empty the given container component, and then insert the markup into the given container
- call
setInnerHTML
To insert the markup into the given container and cache the virtual DOM to the actual DOM node
For the initial rendering, the first method is performed, emptying the container components and mounting the markup into the actual DOM. The call stack: DOMLazyTree insertTreeBefore – > insertTreeChildren
function insertTreeChildren(tree) { if (! enableLazy) { return; } var node = tree.node; var children = tree.children; if (children.length) { for (var i = 0; i < children.length; i++) { insertTreeBefore(node, children[i], null); } } else if (tree.html ! = null) { setInnerHTML(node, tree.html); } else if (tree.text ! = null) { setTextContent(node, tree.text); }}Copy the code
Mount all child components recursively into the DOM.
conclusion
The React and ReactDOM code is very abstract, which makes the code very complicated to read. This article is also limited to the overall process and does not delve into details because there are too many details.
Finally, a reactdom. render post-execution process diagram is sorted out, which can help to understand its whole process to a certain extent: the diagram is very large, and it is not clear even after clicking to enlarge it. It is suggested to save it and browse it locally, it will be clearer.
ReactDOM.render
Related articles
- React source code – import file
- React – ReactBaseClasses
- React – ReactChildren
- React – ReactElement
- React source code – onlyChildren