@samitouri / QOS-React-2 / commits / 690409a912

Replace ReactComponentMixin with ReactReconciler

Instead of putting the shared code in a base class method, we use a wrapper call around all invokations. That way they're free to add code before AND after the non-shared code. That way we ensure that component extensions don't need to implement ReactComponentMixin and do super() calls into it. This helps to create a tighter API for custom component extensions. This provides the first step towards moving these methods to static methods which allows to use a different dispatch mechanism instead of virtual method calls. E.g. pattern matching.

Sebastian Markbage committed Jan 23, 2015 at 10:04 UTC 690409a91239e04a38f3f819f8c5b13a6c60b114
14 files changed +191 -305
src/browser/ui/React.js
+1 -2
@@ -16,7 +16,6 @@
16 var DOMPropertyOperations = require('DOMPropertyOperations');
17 var EventPluginUtils = require('EventPluginUtils');
18 var ReactChildren = require('ReactChildren');
19 -var ReactComponent = require('ReactComponent');
19 var ReactComponentBase = require('ReactComponentBase');
20 var ReactClass = require('ReactClass');
21 var ReactContext = require('ReactContext');
@@ -86,7 +85,7 @@ if (
85 typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
86 typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') {
87 __REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
89 - Component: ReactComponent,
88 + // TODO: Inject a hook for notifying devtools of updates
89 CurrentOwner: ReactCurrentOwner,
90 DOMComponent: ReactDOMComponent,
91 DOMPropertyOperations: DOMPropertyOperations,
src/browser/ui/ReactDOMComponent.js
+4 -29
@@ -17,7 +17,6 @@
17 var CSSPropertyOperations = require('CSSPropertyOperations');
18 var DOMProperty = require('DOMProperty');
19 var DOMPropertyOperations = require('DOMPropertyOperations');
20 -var ReactComponent = require('ReactComponent');
20 var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
21 var ReactMount = require('ReactMount');
22 var ReactMultiChild = require('ReactMultiChild');
@@ -162,7 +161,6 @@ function validateDangerousTag(tag) {
161 * object mapping of style properties to values.
162 *
163 * @constructor ReactDOMComponent
165 - * @extends ReactComponent
164 * @extends ReactMultiChild
165 */
166 function ReactDOMComponent(tag) {
@@ -177,6 +175,10 @@ ReactDOMComponent.displayName = 'ReactDOMComponent';
175
176 ReactDOMComponent.Mixin = {
177
178 + construct: function(element) {
179 + this._currentElement = element;
180 + },
181 +
182 /**
183 * Generates root tag markup then recurses. This method has side effects and
184 * is not idempotent.
@@ -187,12 +189,6 @@ ReactDOMComponent.Mixin = {
189 * @return {string} The computed markup.
190 */
191 mountComponent: function(rootID, transaction, context) {
190 - ReactComponent.Mixin.mountComponent.call(
191 - this,
192 - rootID,
193 - transaction,
194 - context
195 - );
192 this._rootNodeID = rootID;
193 assertValidProps(this._currentElement.props);
194 var closeTag = omittedCloseTags[this._tag] ? '' : '</' + this._tag + '>';
@@ -300,18 +296,6 @@ ReactDOMComponent.Mixin = {
296 },
297
298 receiveComponent: function(nextElement, transaction, context) {
303 - if (nextElement === this._currentElement &&
304 - nextElement._owner != null) {
305 - // Since elements are immutable after the owner is rendered,
306 - // we can do a cheap identity compare here to determine if this is a
307 - // superfluous reconcile. It's possible for state to be mutable but such
308 - // change should trigger an update of the owner which would recreate
309 - // the element. We explicitly check for the existence of an owner since
310 - // it's possible for an element created outside a composite to be
311 - // deeply mutated and reused.
312 - return;
313 - }
314 -
299 var prevElement = this._currentElement;
300 this._currentElement = nextElement;
301 this.updateComponent(transaction, prevElement, nextElement, context);
@@ -329,13 +313,6 @@ ReactDOMComponent.Mixin = {
313 */
314 updateComponent: function(transaction, prevElement, nextElement, context) {
315 assertValidProps(this._currentElement.props);
332 - ReactComponent.Mixin.updateComponent.call(
333 - this,
334 - transaction,
335 - prevElement,
336 - nextElement,
337 - context
338 - );
316 this._updateDOMProperties(prevElement.props, transaction);
317 this._updateDOMChildren(prevElement.props, transaction, context);
318 },
@@ -498,7 +475,6 @@ ReactDOMComponent.Mixin = {
475 unmountComponent: function() {
476 this.unmountChildren();
477 ReactBrowserEventEmitter.deleteAllListeners(this._rootNodeID);
501 - ReactComponent.Mixin.unmountComponent.call(this);
478 ReactMount.purgeID(this._rootNodeID);
479 this._rootNodeID = null;
480 }
@@ -512,7 +488,6 @@ ReactPerf.measureMethods(ReactDOMComponent, 'ReactDOMComponent', {
488
489 assign(
490 ReactDOMComponent.prototype,
515 - ReactComponent.Mixin,
491 ReactDOMComponent.Mixin,
492 ReactMultiChild.Mixin
493 );
src/browser/ui/ReactDOMTextComponent.js
-7
@@ -107,13 +107,6 @@ assign(ReactDOMTextComponent.prototype, {
107 }
108 },
109
110 - updateComponent: function() {
111 - invariant(
112 - false,
113 - 'ReactDOMTextComponent: updateComponent() should never be called'
114 - );
115 - },
116 -
110 unmountComponent: function() {
111 // TODO: Is this necessary?
112 ReactComponentBrowserEnvironment.unmountIDFromEnvironment(this._rootNodeID);
src/browser/ui/ReactMount.js
+5 -2
@@ -21,6 +21,7 @@ var ReactInstanceHandles = require('ReactInstanceHandles');
21 var ReactInstanceMap = require('ReactInstanceMap');
22 var ReactMarkupChecksum = require('ReactMarkupChecksum');
23 var ReactPerf = require('ReactPerf');
24 +var ReactReconciler = require('ReactReconciler');
25 var ReactUpdates = require('ReactUpdates');
26
27 var emptyObject = require('emptyObject');
@@ -241,7 +242,9 @@ function mountComponentIntoNode(
242 container,
243 transaction,
244 shouldReuseMarkup) {
244 - var markup = this.mountComponent(rootID, transaction, emptyObject);
245 + var markup = ReactReconciler.mountComponent(
246 + this, rootID, transaction, emptyObject
247 + );
248 this._isTopLevel = true;
249 ReactMount._mountImageIntoNode(markup, container, shouldReuseMarkup);
250 }
@@ -553,7 +556,7 @@ var ReactMount = {
556 * @see {ReactMount.unmountComponentAtNode}
557 */
558 unmountComponentFromNode: function(instance, container) {
556 - instance.unmountComponent();
559 + ReactReconciler.unmountComponent(instance);
560
561 if (container.nodeType === DOC_NODE_TYPE) {
562 container = container.documentElement;
src/browser/ui/__tests__/ReactDOMComponent-test.js
+1 -3
@@ -308,15 +308,13 @@ describe('ReactDOMComponent', function() {
308 beforeEach(function() {
309 require('mock-modules').dumpCache();
310
311 - var ReactComponent = require('ReactComponent');
311 var ReactMultiChild = require('ReactMultiChild');
312 var ReactDOMComponent = require('ReactDOMComponent');
313 var ReactReconcileTransaction = require('ReactReconcileTransaction');
314
315 var StubNativeComponent = function(element) {
317 - ReactComponent.Mixin.construct.call(this, element);
316 + this._currentElement = element;
317 };
319 - assign(StubNativeComponent.prototype, ReactComponent.Mixin);
318 assign(StubNativeComponent.prototype, ReactDOMComponent.Mixin);
319 assign(StubNativeComponent.prototype, ReactMultiChild.Mixin);
320
src/core/ReactChildReconciler.js
+8 -4
@@ -12,6 +12,8 @@
12
13 'use strict';
14
15 +var ReactReconciler = require('ReactReconciler');
16 +
17 var flattenChildren = require('flattenChildren');
18 var instantiateReactComponent = require('instantiateReactComponent');
19 var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
@@ -78,11 +80,13 @@ var ReactChildReconciler = {
80 var prevElement = prevChild && prevChild._currentElement;
81 var nextElement = nextChildren[name];
82 if (shouldUpdateReactComponent(prevElement, nextElement)) {
81 - prevChild.receiveComponent(nextElement, transaction, context);
83 + ReactReconciler.receiveComponent(
84 + prevChild, nextElement, transaction, context
85 + );
86 nextChildren[name] = prevChild;
87 } else {
88 if (prevChild) {
85 - prevChild.unmountComponent();
89 + ReactReconciler.unmountComponent(prevChild, name);
90 }
91 // The child must be instantiated before it's mounted.
92 var nextChildInstance = instantiateReactComponent(
@@ -96,7 +100,7 @@ var ReactChildReconciler = {
100 for (name in prevChildren) {
101 if (prevChildren.hasOwnProperty(name) &&
102 !(nextChildren && nextChildren.hasOwnProperty(name))) {
99 - prevChildren[name].unmountComponent();
103 + ReactReconciler.unmountComponent(prevChildren[name]);
104 }
105 }
106 return nextChildren;
@@ -112,7 +116,7 @@ var ReactChildReconciler = {
116 unmountChildren: function(renderedChildren) {
117 for (var name in renderedChildren) {
118 var renderedChild = renderedChildren[name];
115 - renderedChild.unmountComponent();
119 + ReactReconciler.unmountComponent(renderedChild);
120 }
121 }
122
src/core/ReactComponent.js deleted
-147
@@ -1,147 +0,0 @@
1 -/**
2 - * Copyright 2013-2014, Facebook, Inc.
3 - * All rights reserved.
4 - *
5 - * This source code is licensed under the BSD-style license found in the
6 - * LICENSE file in the root directory of this source tree. An additional grant
7 - * of patent rights can be found in the PATENTS file in the same directory.
8 - *
9 - * @providesModule ReactComponent
10 - */
11 -
12 -'use strict';
13 -
14 -var ReactElementValidator = require('ReactElementValidator');
15 -
16 -var invariant = require('invariant');
17 -
18 -/**
19 - * Components are the basic units of composition in React.
20 - *
21 - * Every component accepts a set of keyed input parameters known as "props" that
22 - * are initialized by the constructor. Once a component is mounted, the props
23 - * can be mutated using `setProps` or `replaceProps`.
24 - *
25 - * Every component is capable of the following operations:
26 - *
27 - * `mountComponent`
28 - * Initializes the component, renders markup, and registers event listeners.
29 - *
30 - * `receiveComponent`
31 - * Updates the rendered DOM nodes to match the given component.
32 - *
33 - * `unmountComponent`
34 - * Releases any resources allocated by this component.
35 - *
36 - * Components can also be "owned" by other components. Being owned by another
37 - * component means being constructed by that component. This is different from
38 - * being the child of a component, which means having a DOM representation that
39 - * is a child of the DOM representation of that component.
40 - *
41 - * @class ReactComponent
42 - */
43 -var ReactComponent = {
44 -
45 - /**
46 - * Injected module that provides ability to mutate individual properties.
47 - * Injected into the base class because many different subclasses need access
48 - * to this.
49 - *
50 - * @internal
51 - */
52 - BackendIDOperations: null,
53 -
54 - /**
55 - * Base functionality for every ReactComponent constructor. Mixed into the
56 - * `ReactComponent` prototype, but exposed statically for easy access.
57 - *
58 - * @lends {ReactComponent.prototype}
59 - */
60 - Mixin: {
61 -
62 - /**
63 - * Base constructor for all React components.
64 - *
65 - * Subclasses that override this method should make sure to invoke
66 - * `ReactComponent.Mixin.construct.call(this, ...)`.
67 - *
68 - * @param {ReactElement} element
69 - * @internal
70 - */
71 - construct: function(element) {
72 - // We keep the old element and a reference to the pending element
73 - // to track updates.
74 - this._currentElement = element;
75 - // These two fields are used by the DOM and ART diffing algorithms
76 - // respectively. Instead of using expandos on components, we should be
77 - // storing the state needed by the diffing algorithms elsewhere.
78 - this._mountIndex = 0;
79 - this._mountImage = null;
80 - },
81 -
82 - /**
83 - * Initializes the component, renders markup, and registers event listeners.
84 - *
85 - * NOTE: This does not insert any nodes into the DOM.
86 - *
87 - * Subclasses that override this method should make sure to invoke
88 - * `ReactComponent.Mixin.mountComponent.call(this, ...)`.
89 - *
90 - * @param {string} rootID DOM ID of the root node.
91 - * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
92 - * @return {?string} Rendered markup to be inserted into the DOM.
93 - * @internal
94 - */
95 - mountComponent: function(rootID, transaction, context) {
96 - if (__DEV__) {
97 - ReactElementValidator.checkAndWarnForMutatedProps(this._currentElement);
98 - }
99 - // Effectively: return '';
100 - },
101 -
102 - /**
103 - * Releases any resources allocated by `mountComponent`.
104 - *
105 - * NOTE: This does not remove any nodes from the DOM.
106 - *
107 - * Subclasses that override this method should make sure to invoke
108 - * `ReactComponent.Mixin.unmountComponent.call(this)`.
109 - *
110 - * @internal
111 - */
112 - unmountComponent: function() {
113 - },
114 -
115 - /**
116 - * Updates the component's currently mounted representation.
117 - *
118 - * @param {ReactReconcileTransaction} transaction
119 - * @param {object} prevElement
120 - * @param {object} nextElement
121 - * @internal
122 - */
123 - updateComponent: function(transaction, prevElement, nextElement, context) {
124 - if (__DEV__) {
125 - ReactElementValidator.checkAndWarnForMutatedProps(nextElement);
126 - }
127 - },
128 -
129 - /**
130 - * Get the publicly accessible representation of this component - i.e. what
131 - * is exposed by refs and returned by React.render. Can be null for
132 - * stateless components.
133 - *
134 - * @return {?ReactComponent} the actual sibling Component.
135 - * @internal
136 - */
137 - getPublicInstance: function() {
138 - invariant(
139 - false,
140 - 'getPublicInstance should never be called on the base class. It must ' +
141 - 'be overridden.'
142 - );
143 - }
144 - }
145 -};
146 -
147 -module.exports = ReactComponent;
src/core/ReactCompositeComponent.js
+53 -102
@@ -11,17 +11,17 @@
11
12 'use strict';
13
14 -var ReactComponent = require('ReactComponent');
14 var ReactComponentEnvironment = require('ReactComponentEnvironment');
15 var ReactContext = require('ReactContext');
16 var ReactCurrentOwner = require('ReactCurrentOwner');
17 var ReactElement = require('ReactElement');
18 +var ReactElementValidator = require('ReactElementValidator');
19 var ReactInstanceMap = require('ReactInstanceMap');
20 var ReactNativeComponent = require('ReactNativeComponent');
21 var ReactPerf = require('ReactPerf');
22 var ReactPropTypeLocations = require('ReactPropTypeLocations');
23 var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
24 -var ReactRef = require('ReactRef');
24 +var ReactReconciler = require('ReactReconciler');
25 var ReactUpdates = require('ReactUpdates');
26
27 var assign = require('Object.assign');
@@ -97,8 +97,7 @@ var nextMountID = 1;
97 /**
98 * @lends {ReactCompositeComponent.prototype}
99 */
100 -var ReactCompositeComponentMixin = assign({},
101 - ReactComponent.Mixin, {
100 +var ReactCompositeComponentMixin = {
101
102 /**
103 * Base constructor for all composite component.
@@ -108,6 +107,8 @@ var ReactCompositeComponentMixin = assign({},
107 * @internal
108 */
109 construct: function(element) {
110 + this._currentElement = element;
111 +
112 this._rootNodeID = null;
113 this._instance = null;
114
@@ -119,9 +120,6 @@ var ReactCompositeComponentMixin = assign({},
120
121 this._renderedComponent = null;
122
122 - // Children can be either an array or more than one argument
123 - ReactComponent.Mixin.construct.apply(this, arguments);
124 -
123 this._context = null;
124 this._mountOrder = 0;
125 this._isTopLevel = false;
@@ -150,13 +148,6 @@ var ReactCompositeComponentMixin = assign({},
148 * @internal
149 */
150 mountComponent: function(rootID, transaction, context) {
153 - ReactComponent.Mixin.mountComponent.call(
154 - this,
155 - rootID,
156 - transaction,
157 - context
158 - );
159 -
151 this._context = context;
152 this._mountOrder = nextMountID++;
153 this._rootNodeID = rootID;
@@ -253,7 +244,8 @@ var ReactCompositeComponentMixin = assign({},
244
245 // Done with mounting, `setState` will now trigger UI changes.
246 this._compositeLifeCycleState = null;
256 - var markup = this._renderedComponent.mountComponent(
247 + var markup = ReactReconciler.mountComponent(
248 + this._renderedComponent,
249 rootID,
250 transaction,
251 this._processChildContext(context)
@@ -261,16 +253,8 @@ var ReactCompositeComponentMixin = assign({},
253 if (inst.componentDidMount) {
254 transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
255 }
264 - transaction.getReactMountReady().enqueue(this.attachRefs, this);
265 - return markup;
266 - },
256
268 - /**
269 - * Helper to call ReactRef.attachRefs with this composite component, split out
270 - * to avoid allocations in the transaction mount-ready queue.
271 - */
272 - attachRefs: function() {
273 - ReactRef.attachRefs(this, this._currentElement);
257 + return markup;
258 },
259
260 /**
@@ -282,15 +266,13 @@ var ReactCompositeComponentMixin = assign({},
266 unmountComponent: function() {
267 var inst = this._instance;
268
285 - ReactRef.detachRefs(this, this._currentElement);
286 -
269 this._compositeLifeCycleState = CompositeLifeCycle.UNMOUNTING;
270 if (inst.componentWillUnmount) {
271 inst.componentWillUnmount();
272 }
273 this._compositeLifeCycleState = null;
274
293 - this._renderedComponent.unmountComponent();
275 + ReactReconciler.unmountComponent(this._renderedComponent);
276 this._renderedComponent = null;
277
278 // Reset pending fields
@@ -299,8 +281,6 @@ var ReactCompositeComponentMixin = assign({},
281 this._pendingCallbacks = null;
282 this._pendingElement = null;
283
302 - ReactComponent.Mixin.unmountComponent.call(this);
303 -
284 ReactComponentEnvironment.unmountIDFromEnvironment(this._rootNodeID);
285
286 this._context = null;
@@ -636,32 +616,7 @@ var ReactCompositeComponentMixin = assign({},
616 }
617 },
618
639 - receiveComponent: function(nextElement, transaction, context) {
640 - if (nextElement === this._currentElement &&
641 - nextElement._owner != null) {
642 - // Since elements are immutable after the owner is rendered,
643 - // we can do a cheap identity compare here to determine if this is a
644 - // superfluous reconcile. It's possible for state to be mutable but such
645 - // change should trigger an update of the owner which would recreate
646 - // the element. We explicitly check for the existence of an owner since
647 - // it's possible for an element created outside a composite to be
648 - // deeply mutated and reused.
649 - return;
650 - }
651 -
652 - this._pendingElement = nextElement;
653 - this._pendingContext = context;
654 - this.performUpdateIfNecessary(transaction);
655 - },
656 -
657 - /**
658 - * If any of `_pendingElement`, `_pendingState`, or `_pendingForceUpdate`
659 - * is set, update the component.
660 - *
661 - * @param {ReactReconcileTransaction} transaction
662 - * @internal
663 - */
664 - performUpdateIfNecessary: function(transaction) {
619 + receiveComponent: function(nextElement, transaction, nextContext) {
620 var compositeLifeCycleState = this._compositeLifeCycleState;
621 // Do not trigger a state transition if we are in the middle of mounting or
622 // receiving props because both of those will already be doing this.
@@ -670,26 +625,11 @@ var ReactCompositeComponentMixin = assign({},
625 return;
626 }
627
673 - if (this._pendingElement == null &&
674 - this._pendingState == null &&
675 - this._pendingContext == null &&
676 - !this._pendingForceUpdate) {
677 - return;
678 - }
679 -
628 var prevElement = this._currentElement;
681 - var nextElement = prevElement;
682 - if (this._pendingElement != null) {
683 - nextElement = this._pendingElement;
684 - this._pendingElement = null;
685 - }
686 -
629 var prevContext = this._context;
688 - var nextContext = prevContext;
689 - if (this._pendingContext != null) {
690 - nextContext = this._pendingContext;
691 - this._pendingContext = null;
692 - }
630 +
631 + this._pendingElement = null;
632 + this._pendingContext = null;
633
634 this.updateComponent(
635 transaction,
@@ -700,6 +640,40 @@ var ReactCompositeComponentMixin = assign({},
640 );
641 },
642
643 + /**
644 + * If any of `_pendingElement`, `_pendingState`, or `_pendingForceUpdate`
645 + * is set, update the component.
646 + *
647 + * @param {ReactReconcileTransaction} transaction
648 + * @internal
649 + */
650 + performUpdateIfNecessary: function(transaction) {
651 + if (this._pendingElement != null || this._pendingContext != null) {
652 + ReactReconciler.receiveComponent(
653 + this,
654 + this._pendingElement || this._currentElement,
655 + transaction,
656 + this._pendingContext || this._context
657 + );
658 + }
659 +
660 + if (this._pendingState != null || this._pendingForceUpdate) {
661 + if (__DEV__) {
662 + ReactElementValidator.checkAndWarnForMutatedProps(
663 + this._currentElement
664 + );
665 + }
666 +
667 + this.updateComponent(
668 + transaction,
669 + this._currentElement,
670 + this._currentElement,
671 + this._context,
672 + this._context
673 + );
674 + }
675 + },
676 +
677 /**
678 * Compare two contexts, warning if they are different
679 * TODO: Remove this check when owner-context is removed
@@ -746,15 +720,6 @@ var ReactCompositeComponentMixin = assign({},
720 prevUnmaskedContext,
721 nextUnmaskedContext
722 ) {
749 - ReactComponent.Mixin.updateComponent.call(
750 - this,
751 - transaction,
752 - prevParentElement,
753 - nextParentElement,
754 - prevUnmaskedContext,
755 - nextUnmaskedContext
756 - );
757 -
723 var inst = this._instance;
724
725 var prevContext = inst.context;
@@ -762,16 +727,6 @@ var ReactCompositeComponentMixin = assign({},
727 var nextContext = prevContext;
728 var nextProps = prevProps;
729
765 - var refsChanged = ReactRef.shouldUpdateRefs(
766 - this,
767 - prevParentElement,
768 - nextParentElement
769 - );
770 -
771 - if (refsChanged) {
772 - ReactRef.detachRefs(this, prevParentElement);
773 - }
774 -
730 // Distinguish between a props update versus a simple state update
731 if (prevParentElement !== nextParentElement) {
732 nextContext = this._processContext(nextParentElement._context);
@@ -829,11 +784,6 @@ var ReactCompositeComponentMixin = assign({},
784 inst.state = nextState;
785 inst.context = nextContext;
786 }
832 -
833 - // Update refs regardless of what shouldComponentUpdate returns
834 - if (refsChanged) {
835 - transaction.getReactMountReady().enqueue(this.attachRefs, this);
836 - }
787 },
788
789 /**
@@ -894,7 +844,8 @@ var ReactCompositeComponentMixin = assign({},
844 var prevRenderedElement = prevComponentInstance._currentElement;
845 var nextRenderedElement = this._renderValidatedComponent();
846 if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
897 - prevComponentInstance.receiveComponent(
847 + ReactReconciler.receiveComponent(
848 + prevComponentInstance,
849 nextRenderedElement,
850 transaction,
851 this._processChildContext(context)
@@ -903,13 +854,14 @@ var ReactCompositeComponentMixin = assign({},
854 // These two IDs are actually the same! But nothing should rely on that.
855 var thisID = this._rootNodeID;
856 var prevComponentID = prevComponentInstance._rootNodeID;
906 - prevComponentInstance.unmountComponent();
857 + ReactReconciler.unmountComponent(prevComponentInstance);
858
859 this._renderedComponent = this._instantiateReactComponent(
860 nextRenderedElement,
861 this._currentElement.type
862 );
912 - var nextMarkup = this._renderedComponent.mountComponent(
863 + var nextMarkup = ReactReconciler.mountComponent(
864 + this._renderedComponent,
865 thisID,
866 transaction,
867 context
@@ -957,7 +909,6 @@ var ReactCompositeComponentMixin = assign({},
909 this._currentElement._context
910 );
911 ReactCurrentOwner.current = this;
960 - var inst = this._instance;
912 try {
913 renderedComponent =
914 this._renderValidatedComponentWithoutOwnerOrContext();
@@ -1033,7 +984,7 @@ var ReactCompositeComponentMixin = assign({},
984 // Stub
985 _instantiateReactComponent: null
986
1036 -});
987 +};
988
989 ReactPerf.measureMethods(
990 ReactCompositeComponentMixin,
src/core/ReactMultiChild.js
+6 -3
@@ -15,6 +15,7 @@
15 var ReactComponentEnvironment = require('ReactComponentEnvironment');
16 var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
17
18 +var ReactReconciler = require('ReactReconciler');
19 var ReactChildReconciler = require('ReactChildReconciler');
20
21 /**
@@ -184,11 +185,12 @@ var ReactMultiChild = {
185 var mountImages = [];
186 var index = 0;
187 for (var name in children) {
187 - var child = children[name];
188 if (children.hasOwnProperty(name)) {
189 + var child = children[name];
190 // Inlined for performance, see `ReactInstanceHandles.createReactID`.
191 var rootID = this._rootNodeID + name;
191 - var mountImage = child.mountComponent(
192 + var mountImage = ReactReconciler.mountComponent(
193 + child,
194 rootID,
195 transaction,
196 context
@@ -395,7 +397,8 @@ var ReactMultiChild = {
397 context) {
398 // Inlined for performance, see `ReactInstanceHandles.createReactID`.
399 var rootID = this._rootNodeID + name;
398 - var mountImage = child.mountComponent(
400 + var mountImage = ReactReconciler.mountComponent(
401 + child,
402 rootID,
403 transaction,
404 context
src/core/ReactReconciler.js new
+107
@@ -0,0 +1,107 @@
1 +/**
2 + * Copyright 2013-2014, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @providesModule ReactReconciler
10 + */
11 +
12 +'use strict';
13 +
14 +var ReactRef = require('ReactRef');
15 +var ReactElementValidator = require('ReactElementValidator');
16 +
17 +/**
18 + * Helper to call ReactRef.attachRefs with this composite component, split out
19 + * to avoid allocations in the transaction mount-ready queue.
20 + */
21 +function attachRefs() {
22 + ReactRef.attachRefs(this, this._currentElement);
23 +}
24 +
25 +var ReactReconciler = {
26 +
27 + /**
28 + * Initializes the component, renders markup, and registers event listeners.
29 + *
30 + * @param {ReactComponent} internalInstance
31 + * @param {string} rootID DOM ID of the root node.
32 + * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
33 + * @return {?string} Rendered markup to be inserted into the DOM.
34 + * @final
35 + * @internal
36 + */
37 + mountComponent: function(internalInstance, rootID, transaction, context) {
38 + var markup = internalInstance.mountComponent(rootID, transaction, context);
39 + if (__DEV__) {
40 + ReactElementValidator.checkAndWarnForMutatedProps(
41 + internalInstance._currentElement
42 + );
43 + }
44 + transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
45 + return markup;
46 + },
47 +
48 + /**
49 + * Releases any resources allocated by `mountComponent`.
50 + *
51 + * @final
52 + * @internal
53 + */
54 + unmountComponent: function(internalInstance) {
55 + ReactRef.detachRefs(internalInstance, internalInstance._currentElement);
56 + internalInstance.unmountComponent();
57 + },
58 +
59 + /**
60 + * Update a component using a new element.
61 + *
62 + * @param {ReactComponent} internalInstance
63 + * @param {ReactElement} nextElement
64 + * @param {ReactReconcileTransaction} transaction
65 + * @param {object} context
66 + * @internal
67 + */
68 + receiveComponent: function(
69 + internalInstance, nextElement, transaction, context
70 + ) {
71 + var prevElement = internalInstance._currentElement;
72 +
73 + if (nextElement === prevElement && nextElement._owner != null) {
74 + // Since elements are immutable after the owner is rendered,
75 + // we can do a cheap identity compare here to determine if this is a
76 + // superfluous reconcile. It's possible for state to be mutable but such
77 + // change should trigger an update of the owner which would recreate
78 + // the element. We explicitly check for the existence of an owner since
79 + // it's possible for an element created outside a composite to be
80 + // deeply mutated and reused.
81 + return;
82 + }
83 +
84 + if (__DEV__) {
85 + ReactElementValidator.checkAndWarnForMutatedProps(nextElement);
86 + }
87 +
88 + var refsChanged = ReactRef.shouldUpdateRefs(
89 + this,
90 + prevElement,
91 + nextElement
92 + );
93 +
94 + if (refsChanged) {
95 + ReactRef.detachRefs(internalInstance, prevElement);
96 + }
97 +
98 + internalInstance.receiveComponent(nextElement, transaction, context);
99 +
100 + if (refsChanged) {
101 + transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
102 + }
103 + }
104 +
105 +};
106 +
107 +module.exports = ReactReconciler;
src/core/__tests__/ReactComponentLifeCycle-test.js
-2
@@ -15,7 +15,6 @@ var keyMirror = require('keyMirror');
15
16 var React;
17 var ReactTestUtils;
18 -var ReactComponent;
18 var ReactCompositeComponent;
19 var ReactInstanceMap;
20 var CompositeComponentLifeCycle;
@@ -97,7 +96,6 @@ describe('ReactComponentLifeCycle', function() {
96 require('mock-modules').dumpCache();
97 React = require('React');
98 ReactTestUtils = require('ReactTestUtils');
100 - ReactComponent = require('ReactComponent');
99 ReactCompositeComponent = require('ReactCompositeComponent');
100 CompositeComponentLifeCycle = ReactCompositeComponent.LifeCycle;
101
src/core/__tests__/ReactCompositeComponent-test.js
-2
@@ -14,7 +14,6 @@
14 var ChildUpdates;
15 var MorphingComponent;
16 var React;
17 -var ReactComponent;
17 var ReactCurrentOwner;
18 var ReactDoNotBindDeprecated;
19 var ReactMount;
@@ -36,7 +35,6 @@ describe('ReactCompositeComponent', function() {
35
36 reactComponentExpect = require('reactComponentExpect');
37 React = require('React');
39 - ReactComponent = require('ReactComponent');
38 ReactCurrentOwner = require('ReactCurrentOwner');
39 ReactDoNotBindDeprecated = require('ReactDoNotBindDeprecated');
40 ReactPropTypes = require('ReactPropTypes');
src/core/__tests__/ReactMockedComponent-test.js
-2
@@ -12,7 +12,6 @@
12 'use strict';
13
14 var React;
15 -var ReactComponent;
15 var ReactTestUtils;
16
17 var mocks;
@@ -27,7 +26,6 @@ describe('ReactMockedComponent', function() {
26 mocks = require('mocks');
27
28 React = require('React');
30 - ReactComponent = require('ReactComponent');
29 ReactTestUtils = require('ReactTestUtils');
30
31 OriginalComponent = React.createClass({
src/core/instantiateReactComponent.js
+6
@@ -108,6 +108,12 @@ function instantiateReactComponent(node, parentCompositeType) {
108 // Sets up the instance. This can probably just move into the constructor now.
109 instance.construct(node);
110
111 + // These two fields are used by the DOM and ART diffing algorithms
112 + // respectively. Instead of using expandos on components, we should be
113 + // storing the state needed by the diffing algorithms elsewhere.
114 + instance._mountIndex = 0;
115 + instance._mountImage = null;
116 +
117 // Internal instances should fully constructed at this point, so they should
118 // not get any new fields added to them at this point.
119 if (__DEV__) {