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__) {