From 0480a8c1520dd328ed7ee839adb87170579b14a4 Mon Sep 17 00:00:00 2001 From: Michael Ridgway Date: Sat, 6 Feb 2016 02:04:59 -0800 Subject: [PATCH 1/4] Move component recursion to ReactReconciler --- .../dom/server/ReactServerRendering.js | 4 +- src/renderers/dom/shared/ReactDOMComponent.js | 13 ++- .../dom/shared/ReactDOMTextComponent.js | 2 +- .../reconciler/ReactCompositeComponent.js | 10 +-- .../shared/reconciler/ReactMultiChild.js | 9 +- .../shared/reconciler/ReactReconciler.js | 82 +++++++++++++++++-- .../reconciler/ReactSimpleEmptyComponent.js | 8 +- 7 files changed, 88 insertions(+), 40 deletions(-) diff --git a/src/renderers/dom/server/ReactServerRendering.js b/src/renderers/dom/server/ReactServerRendering.js index ff769f2bd64a8..bf27e466870f1 100644 --- a/src/renderers/dom/server/ReactServerRendering.js +++ b/src/renderers/dom/server/ReactServerRendering.js @@ -11,6 +11,7 @@ 'use strict'; var ReactDOMContainerInfo = require('ReactDOMContainerInfo'); +var ReactReconciler = require('ReactReconciler'); var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy'); var ReactElement = require('ReactElement'); var ReactMarkupChecksum = require('ReactMarkupChecksum'); @@ -41,7 +42,8 @@ function renderToStringImpl(element, makeStaticMarkup) { return transaction.perform(function() { var componentInstance = instantiateReactComponent(element); - var markup = componentInstance.mountComponent( + var markup = ReactReconciler.mountComponent( + componentInstance, transaction, null, ReactDOMContainerInfo(), diff --git a/src/renderers/dom/shared/ReactDOMComponent.js b/src/renderers/dom/shared/ReactDOMComponent.js index 1377956636b88..57cd5400dd4b4 100644 --- a/src/renderers/dom/shared/ReactDOMComponent.js +++ b/src/renderers/dom/shared/ReactDOMComponent.js @@ -571,7 +571,7 @@ ReactDOMComponent.Mixin = { mountImage = tagOpen + '/>'; } else { mountImage = - tagOpen + '>' + tagContent + ''; + [tagOpen + '>'].concat(tagContent, ''); } } @@ -691,7 +691,7 @@ ReactDOMComponent.Mixin = { transaction, context ); - ret = mountImages.join(''); + ret = mountImages; } } if (newlineEatingTags[this._tag] && ret.charAt(0) === '\n') { @@ -716,7 +716,7 @@ ReactDOMComponent.Mixin = { var innerHTML = props.dangerouslySetInnerHTML; if (innerHTML != null) { if (innerHTML.__html != null) { - DOMLazyTree.queueHTML(lazyTree, innerHTML.__html); + lazyTree.html = innerHTML.__html; } } else { var contentToUse = @@ -724,16 +724,15 @@ ReactDOMComponent.Mixin = { var childrenToUse = contentToUse != null ? null : props.children; if (contentToUse != null) { // TODO: Validate that text is allowed as a child of this node - DOMLazyTree.queueText(lazyTree, contentToUse); + lazyTree.text = contentToUse; } else if (childrenToUse != null) { var mountImages = this.mountChildren( childrenToUse, transaction, context ); - for (var i = 0; i < mountImages.length; i++) { - DOMLazyTree.queueChild(lazyTree, mountImages[i]); - } + lazyTree.children = mountImages; + return mountImages; } } }, diff --git a/src/renderers/dom/shared/ReactDOMTextComponent.js b/src/renderers/dom/shared/ReactDOMTextComponent.js index 63d022bf33512..92101448aeea3 100644 --- a/src/renderers/dom/shared/ReactDOMTextComponent.js +++ b/src/renderers/dom/shared/ReactDOMTextComponent.js @@ -97,7 +97,7 @@ assign(ReactDOMTextComponent.prototype, { var el = ownerDocument.createElement('span'); ReactDOMComponentTree.precacheNode(this, el); var lazyTree = DOMLazyTree(el); - DOMLazyTree.queueText(lazyTree, this._stringText); + lazyTree.text = this._stringText; return lazyTree; } else { var escapedText = escapeTextContentForBrowser(this._stringText); diff --git a/src/renderers/shared/reconciler/ReactCompositeComponent.js b/src/renderers/shared/reconciler/ReactCompositeComponent.js index 8914fb6eafdba..a85ff7bc920d1 100644 --- a/src/renderers/shared/reconciler/ReactCompositeComponent.js +++ b/src/renderers/shared/reconciler/ReactCompositeComponent.js @@ -347,15 +347,7 @@ var ReactCompositeComponentMixin = { renderedElement ); - var markup = ReactReconciler.mountComponent( - this._renderedComponent, - transaction, - nativeParent, - nativeContainerInfo, - this._processChildContext(context) - ); - - return markup; + return this._renderedComponent; }, getNativeNode: function() { diff --git a/src/renderers/shared/reconciler/ReactMultiChild.js b/src/renderers/shared/reconciler/ReactMultiChild.js index 7a22c76cea8c3..3136533d04800 100644 --- a/src/renderers/shared/reconciler/ReactMultiChild.js +++ b/src/renderers/shared/reconciler/ReactMultiChild.js @@ -219,15 +219,8 @@ var ReactMultiChild = { for (var name in children) { if (children.hasOwnProperty(name)) { var child = children[name]; - var mountImage = ReactReconciler.mountComponent( - child, - transaction, - this, - this._nativeContainerInfo, - context - ); child._mountIndex = index++; - mountImages.push(mountImage); + mountImages.push(child); } } return mountImages; diff --git a/src/renderers/shared/reconciler/ReactReconciler.js b/src/renderers/shared/reconciler/ReactReconciler.js index 4e1a1d3934ab9..901af8f84679e 100644 --- a/src/renderers/shared/reconciler/ReactReconciler.js +++ b/src/renderers/shared/reconciler/ReactReconciler.js @@ -12,6 +12,7 @@ 'use strict'; var ReactRef = require('ReactRef'); +var DOMLazyTree = require('DOMLazyTree'); /** * Helper to call ReactRef.attachRefs with this composite component, split out @@ -41,17 +42,84 @@ var ReactReconciler = { nativeContainerInfo, context ) { - var markup = internalInstance.mountComponent( - transaction, - nativeParent, - nativeContainerInfo, - context - ); + var child = internalInstance.mountComponent( + transaction, + nativeParent, + nativeContainerInfo, + context + ); if (internalInstance._currentElement && internalInstance._currentElement.ref != null) { transaction.getReactMountReady().enqueue(attachRefs, internalInstance); } - return markup; + + var join = true; + if (!Array.isArray(child)) { + child = [child]; + join = false; + } + var processedChildren = child.map(function(childOfChild) { + return ReactReconciler.processChild( + childOfChild, + internalInstance, + transaction, + nativeParent, + nativeContainerInfo, + context + ); + }); + + return join ? processedChildren.join('') : processedChildren[0]; + }, + + processChild: function( + child, + internalInstance, + transaction, + nativeParent, + nativeContainerInfo, + context + ) { + if ('string' === typeof child || 'number' === typeof child) { + return child; + } else if (child.node) { + // DOMLazyTree + if (child.html) { + // dangerouslySetInnerHTML + DOMLazyTree.queueHTML(child, child.html); + } else if (child.text) { + // ReactTextComponent + DOMLazyTree.queueText(child, child.text); + } else if (child.children) { + // ReactDOMComponent + for (var i=0; i Date: Mon, 8 Feb 2016 14:04:51 -0800 Subject: [PATCH 2/4] Add lazy tree implementation injection --- src/renderers/dom/client/utils/DOMLazyTree.js | 4 ++++ .../dom/shared/ReactDefaultInjection.js | 4 ++++ src/renderers/dom/shared/ReactInjection.js | 2 ++ .../shared/reconciler/ReactReconciler.js | 18 +++++++++++++----- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/renderers/dom/client/utils/DOMLazyTree.js b/src/renderers/dom/client/utils/DOMLazyTree.js index 4fbb2e0443848..1722cda3ec26a 100644 --- a/src/renderers/dom/client/utils/DOMLazyTree.js +++ b/src/renderers/dom/client/utils/DOMLazyTree.js @@ -33,6 +33,8 @@ var enableLazy = ( /\bEdge\/\d/.test(navigator.userAgent) ); +var treeType = 'dom'; + function insertTreeChildren(tree) { if (!enableLazy) { return; @@ -86,6 +88,7 @@ function queueText(tree, text) { function DOMLazyTree(node) { return { + lazy: treeType, node: node, children: [], html: null, @@ -93,6 +96,7 @@ function DOMLazyTree(node) { }; } +DOMLazyTree.treeType = treeType; DOMLazyTree.insertTreeBefore = insertTreeBefore; DOMLazyTree.replaceChildWithTree = replaceChildWithTree; DOMLazyTree.queueChild = queueChild; diff --git a/src/renderers/dom/shared/ReactDefaultInjection.js b/src/renderers/dom/shared/ReactDefaultInjection.js index 09cbf25e485f9..483f72f747fee 100644 --- a/src/renderers/dom/shared/ReactDefaultInjection.js +++ b/src/renderers/dom/shared/ReactDefaultInjection.js @@ -31,6 +31,8 @@ var ReactReconcileTransaction = require('ReactReconcileTransaction'); var SVGDOMPropertyConfig = require('SVGDOMPropertyConfig'); var SelectEventPlugin = require('SelectEventPlugin'); var SimpleEventPlugin = require('SimpleEventPlugin'); +var DOMLazyTree = require('DOMLazyTree'); + var alreadyInjected = false; @@ -92,6 +94,8 @@ function inject() { ReactInjection.Component.injectEnvironment(ReactComponentBrowserEnvironment); + ReactInjection.ReactReconciler.injectLazyTreeImpl(DOMLazyTree); + if (__DEV__) { var url = (ExecutionEnvironment.canUseDOM && window.location.href) || ''; if ((/[?&]react_perf\b/).test(url)) { diff --git a/src/renderers/dom/shared/ReactInjection.js b/src/renderers/dom/shared/ReactInjection.js index 26085eb2f73b3..36d96262f461b 100644 --- a/src/renderers/dom/shared/ReactInjection.js +++ b/src/renderers/dom/shared/ReactInjection.js @@ -20,6 +20,7 @@ var ReactEmptyComponent = require('ReactEmptyComponent'); var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var ReactNativeComponent = require('ReactNativeComponent'); var ReactPerf = require('ReactPerf'); +var ReactReconciler = require('ReactReconciler'); var ReactUpdates = require('ReactUpdates'); var ReactInjection = { @@ -32,6 +33,7 @@ var ReactInjection = { EventEmitter: ReactBrowserEventEmitter.injection, NativeComponent: ReactNativeComponent.injection, Perf: ReactPerf.injection, + ReactReconciler: ReactReconciler.injection, Updates: ReactUpdates.injection, }; diff --git a/src/renderers/shared/reconciler/ReactReconciler.js b/src/renderers/shared/reconciler/ReactReconciler.js index 901af8f84679e..58c085fc06d1d 100644 --- a/src/renderers/shared/reconciler/ReactReconciler.js +++ b/src/renderers/shared/reconciler/ReactReconciler.js @@ -12,7 +12,6 @@ 'use strict'; var ReactRef = require('ReactRef'); -var DOMLazyTree = require('DOMLazyTree'); /** * Helper to call ReactRef.attachRefs with this composite component, split out @@ -22,6 +21,8 @@ function attachRefs() { ReactRef.attachRefs(this, this._currentElement); } +var lazyTreeImpls = {}; + var ReactReconciler = { /** @@ -82,14 +83,15 @@ var ReactReconciler = { ) { if ('string' === typeof child || 'number' === typeof child) { return child; - } else if (child.node) { + } else if (child.lazy) { + var lazyImpl = lazyTreeImpls[child.lazy]; // DOMLazyTree if (child.html) { // dangerouslySetInnerHTML - DOMLazyTree.queueHTML(child, child.html); + lazyImpl.queueHTML(child, child.html); } else if (child.text) { // ReactTextComponent - DOMLazyTree.queueText(child, child.text); + lazyImpl.queueText(child, child.text); } else if (child.children) { // ReactDOMComponent for (var i=0; i Date: Tue, 9 Feb 2016 18:24:24 -0800 Subject: [PATCH 3/4] Add postMount interface for handling post-recursion tasks --- src/renderers/dom/client/utils/DOMLazyTree.js | 6 ++--- src/renderers/dom/shared/ReactDOMComponent.js | 7 +++-- .../reconciler/ReactCompositeComponent.js | 16 +++++++++--- .../shared/reconciler/ReactReconciler.js | 26 +++++++++++++------ 4 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/renderers/dom/client/utils/DOMLazyTree.js b/src/renderers/dom/client/utils/DOMLazyTree.js index 1722cda3ec26a..c04c6dcd142e3 100644 --- a/src/renderers/dom/client/utils/DOMLazyTree.js +++ b/src/renderers/dom/client/utils/DOMLazyTree.js @@ -33,7 +33,7 @@ var enableLazy = ( /\bEdge\/\d/.test(navigator.userAgent) ); -var treeType = 'dom'; +var TREE_TYPE = 'dom'; function insertTreeChildren(tree) { if (!enableLazy) { @@ -88,7 +88,7 @@ function queueText(tree, text) { function DOMLazyTree(node) { return { - lazy: treeType, + lazy: TREE_TYPE, node: node, children: [], html: null, @@ -96,7 +96,7 @@ function DOMLazyTree(node) { }; } -DOMLazyTree.treeType = treeType; +DOMLazyTree.treeType = TREE_TYPE; DOMLazyTree.insertTreeBefore = insertTreeBefore; DOMLazyTree.replaceChildWithTree = replaceChildWithTree; DOMLazyTree.queueChild = queueChild; diff --git a/src/renderers/dom/shared/ReactDOMComponent.js b/src/renderers/dom/shared/ReactDOMComponent.js index 57cd5400dd4b4..26ca54ee99aa1 100644 --- a/src/renderers/dom/shared/ReactDOMComponent.js +++ b/src/renderers/dom/shared/ReactDOMComponent.js @@ -575,6 +575,11 @@ ReactDOMComponent.Mixin = { } } + return mountImage; + }, + + postMount: function(transaction) { + var props = this._currentElement.props; switch (this._tag) { case 'button': case 'input': @@ -588,8 +593,6 @@ ReactDOMComponent.Mixin = { } break; } - - return mountImage; }, /** diff --git a/src/renderers/shared/reconciler/ReactCompositeComponent.js b/src/renderers/shared/reconciler/ReactCompositeComponent.js index a85ff7bc920d1..54a9d8bbda336 100644 --- a/src/renderers/shared/reconciler/ReactCompositeComponent.js +++ b/src/renderers/shared/reconciler/ReactCompositeComponent.js @@ -289,10 +289,6 @@ var ReactCompositeComponentMixin = { markup = this.performInitialMount(renderedElement, nativeParent, nativeContainerInfo, transaction, context); } - if (inst.componentDidMount) { - transaction.getReactMountReady().enqueue(inst.componentDidMount, inst); - } - return markup; }, @@ -354,6 +350,18 @@ var ReactCompositeComponentMixin = { return ReactReconciler.getNativeNode(this._renderedComponent); }, + /** + * Called after recursion of children has completed + * @final + * @internal + */ + postMount: function(transaction) { + var inst = this._instance; + if (inst.componentDidMount) { + transaction.getReactMountReady().enqueue(inst.componentDidMount, inst); + } + }, + /** * Releases any resources allocated by `mountComponent`. * diff --git a/src/renderers/shared/reconciler/ReactReconciler.js b/src/renderers/shared/reconciler/ReactReconciler.js index 58c085fc06d1d..bfdb590382d04 100644 --- a/src/renderers/shared/reconciler/ReactReconciler.js +++ b/src/renderers/shared/reconciler/ReactReconciler.js @@ -44,11 +44,12 @@ var ReactReconciler = { context ) { var child = internalInstance.mountComponent( - transaction, - nativeParent, - nativeContainerInfo, - context - ); + transaction, + nativeParent, + nativeContainerInfo, + context + ); + if (internalInstance._currentElement && internalInstance._currentElement.ref != null) { transaction.getReactMountReady().enqueue(attachRefs, internalInstance); @@ -59,7 +60,7 @@ var ReactReconciler = { child = [child]; join = false; } - var processedChildren = child.map(function(childOfChild) { + var processedChildren = child.map(function processChild(childOfChild) { return ReactReconciler.processChild( childOfChild, internalInstance, @@ -96,7 +97,7 @@ var ReactReconciler = { // ReactDOMComponent for (var i=0; i Date: Tue, 9 Feb 2016 18:47:57 -0800 Subject: [PATCH 4/4] Remove array from ReactSimpleEmptyComponent --- src/renderers/shared/reconciler/ReactSimpleEmptyComponent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderers/shared/reconciler/ReactSimpleEmptyComponent.js b/src/renderers/shared/reconciler/ReactSimpleEmptyComponent.js index 934cfbddade7b..b2656dae27941 100644 --- a/src/renderers/shared/reconciler/ReactSimpleEmptyComponent.js +++ b/src/renderers/shared/reconciler/ReactSimpleEmptyComponent.js @@ -28,7 +28,7 @@ assign(ReactSimpleEmptyComponent.prototype, { nativeContainerInfo, context ) { - return [this._renderedComponent]; + return this._renderedComponent; }, receiveComponent: function() { },