@samitouri / QOS-React-2 / commits / 974a4c84ce

Move mountComponentIntoNode and setProps out of ReactComponent

This is part of moving more logic out of the base classes. setProps, replaceProps etc. are not accessible from anything other than ReactClass so we can safely move it to ReactCompositeComponent. mountComponentIntoNode is tightly coupled to ReactMount. It's part of the outer abstraction, the mount point, not the individual component.

Sebastian Markbage committed Nov 17, 2014 at 00:26 UTC 974a4c84ce9d11d536ad3dd0237cc0017c1a6997
6 files changed +149 -192
src/browser/ui/ReactComponentBrowserEnvironment.js
+1 -74
@@ -14,20 +14,9 @@
14 "use strict";
15
16 var ReactDOMIDOperations = require('ReactDOMIDOperations');
17 -var ReactMarkupChecksum = require('ReactMarkupChecksum');
17 var ReactMount = require('ReactMount');
19 -var ReactPerf = require('ReactPerf');
18 var ReactReconcileTransaction = require('ReactReconcileTransaction');
19
22 -var getReactRootElementInContainer = require('getReactRootElementInContainer');
23 -var invariant = require('invariant');
24 -var setInnerHTML = require('setInnerHTML');
25 -
26 -
27 -var ELEMENT_NODE_TYPE = 1;
28 -var DOC_NODE_TYPE = 9;
29 -
30 -
20 /**
21 * Abstracts away all functionality of `ReactComponent` requires knowledge of
22 * the browser context.
@@ -46,70 +35,8 @@ var ReactComponentBrowserEnvironment = {
35 */
36 unmountIDFromEnvironment: function(rootNodeID) {
37 ReactMount.purgeID(rootNodeID);
49 - },
50 -
51 - /**
52 - * @param {string} markup Markup string to place into the DOM Element.
53 - * @param {DOMElement} container DOM Element to insert markup into.
54 - * @param {boolean} shouldReuseMarkup Should reuse the existing markup in the
55 - * container if possible.
56 - */
57 - mountImageIntoNode: ReactPerf.measure(
58 - 'ReactComponentBrowserEnvironment',
59 - 'mountImageIntoNode',
60 - function(markup, container, shouldReuseMarkup) {
61 - invariant(
62 - container && (
63 - container.nodeType === ELEMENT_NODE_TYPE ||
64 - container.nodeType === DOC_NODE_TYPE
65 - ),
66 - 'mountComponentIntoNode(...): Target container is not valid.'
67 - );
68 -
69 - if (shouldReuseMarkup) {
70 - if (ReactMarkupChecksum.canReuseMarkup(
71 - markup,
72 - getReactRootElementInContainer(container))) {
73 - return;
74 - } else {
75 - invariant(
76 - container.nodeType !== DOC_NODE_TYPE,
77 - 'You\'re trying to render a component to the document using ' +
78 - 'server rendering but the checksum was invalid. This usually ' +
79 - 'means you rendered a different component type or props on ' +
80 - 'the client from the one on the server, or your render() ' +
81 - 'methods are impure. React cannot handle this case due to ' +
82 - 'cross-browser quirks by rendering at the document root. You ' +
83 - 'should look for environment dependent code in your components ' +
84 - 'and ensure the props are the same client and server side.'
85 - );
86 -
87 - if (__DEV__) {
88 - console.warn(
89 - 'React attempted to use reuse markup in a container but the ' +
90 - 'checksum was invalid. This generally means that you are ' +
91 - 'using server rendering and the markup generated on the ' +
92 - 'server was not what the client was expecting. React injected ' +
93 - 'new markup to compensate which works but you have lost many ' +
94 - 'of the benefits of server rendering. Instead, figure out ' +
95 - 'why the markup being generated is different on the client ' +
96 - 'or server.'
97 - );
98 - }
99 - }
100 - }
101 -
102 - invariant(
103 - container.nodeType !== DOC_NODE_TYPE,
104 - 'You\'re trying to render a component to the document but ' +
105 - 'you didn\'t use server rendering. We can\'t do this ' +
106 - 'without using server rendering due to cross-browser quirks. ' +
107 - 'See renderComponentToString() for server rendering.'
108 - );
38 + }
39
110 - setInnerHTML(container, markup);
111 - }
112 - )
40 };
41
42 module.exports = ReactComponentBrowserEnvironment;
src/browser/ui/ReactMount.js
+83 -1
@@ -18,13 +18,16 @@ var ReactElement = require('ReactElement');
18 var ReactEmptyComponent = require('ReactEmptyComponent');
19 var ReactInstanceHandles = require('ReactInstanceHandles');
20 var ReactInstanceMap = require('ReactInstanceMap');
21 +var ReactMarkupChecksum = require('ReactMarkupChecksum');
22 var ReactPerf = require('ReactPerf');
23 +var ReactUpdates = require('ReactUpdates');
24
25 var containsNode = require('containsNode');
26 var deprecated = require('deprecated');
27 var getReactRootElementInContainer = require('getReactRootElementInContainer');
28 var instantiateReactComponent = require('instantiateReactComponent');
29 var invariant = require('invariant');
30 +var setInnerHTML = require('setInnerHTML');
31 var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
32 var warning = require('warning');
33
@@ -208,6 +211,23 @@ function findDeepestCachedAncestor(targetID) {
211 return foundNode;
212 }
213
214 +/**
215 + * Mounts this component and inserts it into the DOM.
216 + *
217 + * @param {string} rootID DOM ID of the root node.
218 + * @param {DOMElement} container DOM element to mount into.
219 + * @param {ReactReconcileTransaction} transaction
220 + * @param {boolean} shouldReuseMarkup If true, do not insert markup
221 + */
222 +function mountComponentIntoNode(
223 + rootID,
224 + container,
225 + transaction,
226 + shouldReuseMarkup) {
227 + var markup = this.mountComponent(rootID, transaction, 0);
228 + ReactMount._mountImageIntoNode(markup, container, shouldReuseMarkup);
229 +}
230 +
231 /**
232 * Mounting is the process of initializing a React component by creatings its
233 * representative DOM elements and inserting them into a supplied `container`.
@@ -321,11 +341,17 @@ var ReactMount = {
341 componentInstance,
342 container
343 );
324 - componentInstance.mountComponentIntoNode(
344 +
345 + var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
346 + transaction.perform(
347 + mountComponentIntoNode,
348 + componentInstance,
349 reactRootID,
350 container,
351 + transaction,
352 shouldReuseMarkup
353 );
354 + ReactUpdates.ReactReconcileTransaction.release(transaction);
355
356 if (__DEV__) {
357 // Record the root element in case it later gets transplanted.
@@ -684,6 +710,62 @@ var ReactMount = {
710 );
711 },
712
713 + _mountImageIntoNode: ReactPerf.measure(
714 + 'ReactMount',
715 + '_mountImageIntoNode',
716 + function(markup, container, shouldReuseMarkup) {
717 + invariant(
718 + container && (
719 + container.nodeType === ELEMENT_NODE_TYPE ||
720 + container.nodeType === DOC_NODE_TYPE
721 + ),
722 + 'mountComponentIntoNode(...): Target container is not valid.'
723 + );
724 +
725 + if (shouldReuseMarkup) {
726 + if (ReactMarkupChecksum.canReuseMarkup(
727 + markup,
728 + getReactRootElementInContainer(container))) {
729 + return;
730 + } else {
731 + invariant(
732 + container.nodeType !== DOC_NODE_TYPE,
733 + 'You\'re trying to render a component to the document using ' +
734 + 'server rendering but the checksum was invalid. This usually ' +
735 + 'means you rendered a different component type or props on ' +
736 + 'the client from the one on the server, or your render() ' +
737 + 'methods are impure. React cannot handle this case due to ' +
738 + 'cross-browser quirks by rendering at the document root. You ' +
739 + 'should look for environment dependent code in your components ' +
740 + 'and ensure the props are the same client and server side.'
741 + );
742 +
743 + if (__DEV__) {
744 + console.warn(
745 + 'React attempted to use reuse markup in a container but the ' +
746 + 'checksum was invalid. This generally means that you are ' +
747 + 'using server rendering and the markup generated on the ' +
748 + 'server was not what the client was expecting. React injected ' +
749 + 'new markup to compensate which works but you have lost many ' +
750 + 'of the benefits of server rendering. Instead, figure out ' +
751 + 'why the markup being generated is different on the client ' +
752 + 'or server.'
753 + );
754 + }
755 + }
756 + }
757 +
758 + invariant(
759 + container.nodeType !== DOC_NODE_TYPE,
760 + 'You\'re trying to render a component to the document but ' +
761 + 'you didn\'t use server rendering. We can\'t do this ' +
762 + 'without using server rendering due to cross-browser quirks. ' +
763 + 'See renderComponentToString() for server rendering.'
764 + );
765 +
766 + setInnerHTML(container, markup);
767 + }
768 + ),
769
770 /**
771 * React ID utilities.
src/core/ReactComponent.js
-115
@@ -31,17 +31,6 @@ var injected = false;
31 */
32 var unmountIDFromEnvironment = null;
33
34 -/**
35 - * The "image" of a component tree, is the platform specific (typically
36 - * serialized) data that represents a tree of lower level UI building blocks.
37 - * On the web, this "image" is HTML markup which describes a construction of
38 - * low level `div` and `span` nodes. Other platforms may have different
39 - * encoding of this "image". This must be injected.
40 - *
41 - * @private
42 - */
43 -var mountImageIntoNode = null;
44 -
34 function attachRef(ref, component, owner) {
35 if (ref instanceof ReactRef) {
36 ReactRef.attachRef(ref, component);
@@ -91,7 +80,6 @@ var ReactComponent = {
80 !injected,
81 'ReactComponent: injectEnvironment() can only be called once.'
82 );
94 - mountImageIntoNode = ReactComponentEnvironment.mountImageIntoNode;
83 unmountIDFromEnvironment =
84 ReactComponentEnvironment.unmountIDFromEnvironment;
85 ReactComponent.BackendIDOperations =
@@ -117,69 +105,6 @@ var ReactComponent = {
105 */
106 Mixin: {
107
120 - /**
121 - * Sets a subset of the props.
122 - *
123 - * @param {object} partialProps Subset of the next props.
124 - * @param {?function} callback Called after props are updated.
125 - * @final
126 - * @public
127 - */
128 - setProps: function(partialProps, callback) {
129 - // Merge with the pending element if it exists, otherwise with existing
130 - // element props.
131 - var element = this._pendingElement || this._currentElement;
132 - this.replaceProps(
133 - assign({}, element.props, partialProps),
134 - callback
135 - );
136 - },
137 -
138 - /**
139 - * Replaces all of the props.
140 - *
141 - * @param {object} props New props.
142 - * @param {?function} callback Called after props are updated.
143 - * @final
144 - * @public
145 - */
146 - replaceProps: function(props, callback) {
147 - invariant(
148 - this._mountDepth === 0,
149 - 'replaceProps(...): You called `setProps` or `replaceProps` on a ' +
150 - 'component with a parent. This is an anti-pattern since props will ' +
151 - 'get reactively updated when rendered. Instead, change the owner\'s ' +
152 - '`render` method to pass the correct value as props to the component ' +
153 - 'where it is created.'
154 - );
155 - // This is a deoptimized path. We optimize for always having an element.
156 - // This creates an extra internal element.
157 - this._pendingElement = ReactElement.cloneAndReplaceProps(
158 - this._pendingElement || this._currentElement,
159 - props
160 - );
161 - ReactUpdates.enqueueUpdate(this, callback);
162 - },
163 -
164 - /**
165 - * Schedule a partial update to the props. Only used for internal testing.
166 - *
167 - * @param {object} partialProps Subset of the next props.
168 - * @param {?function} callback Called after props are updated.
169 - * @final
170 - * @internal
171 - */
172 - _setPropsInternal: function(partialProps, callback) {
173 - // This is a deoptimized path. We optimize for always having an element.
174 - // This creates an extra internal element.
175 - var element = this._pendingElement || this._currentElement;
176 - this._pendingElement = ReactElement.cloneAndReplaceProps(
177 - element,
178 - assign({}, element.props, partialProps)
179 - );
180 - ReactUpdates.enqueueUpdate(this, callback);
181 - },
182 -
108 /**
109 * Base constructor for all React components.
110 *
@@ -312,46 +237,6 @@ var ReactComponent = {
237 }
238 },
239
315 - /**
316 - * Mounts this component and inserts it into the DOM.
317 - *
318 - * @param {string} rootID DOM ID of the root node.
319 - * @param {DOMElement} container DOM element to mount into.
320 - * @param {boolean} shouldReuseMarkup If true, do not insert markup
321 - * @final
322 - * @internal
323 - * @see {ReactMount.render}
324 - */
325 - mountComponentIntoNode: function(rootID, container, shouldReuseMarkup) {
326 - var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
327 - transaction.perform(
328 - this._mountComponentIntoNode,
329 - this,
330 - rootID,
331 - container,
332 - transaction,
333 - shouldReuseMarkup
334 - );
335 - ReactUpdates.ReactReconcileTransaction.release(transaction);
336 - },
337 -
338 - /**
339 - * @param {string} rootID DOM ID of the root node.
340 - * @param {DOMElement} container DOM element to mount into.
341 - * @param {ReactReconcileTransaction} transaction
342 - * @param {boolean} shouldReuseMarkup If true, do not insert markup
343 - * @final
344 - * @private
345 - */
346 - _mountComponentIntoNode: function(
347 - rootID,
348 - container,
349 - transaction,
350 - shouldReuseMarkup) {
351 - var markup = this.mountComponent(rootID, transaction, 0);
352 - mountImageIntoNode(markup, container, shouldReuseMarkup);
353 - },
354 -
240 /**
241 * Get the publicly accessible representation of this component - i.e. what
242 * is exposed by refs and renderComponent. Can be null for stateless
src/core/ReactCompositeComponent.js
+63
@@ -248,6 +248,69 @@ var ReactCompositeComponentMixin = assign({},
248 // TODO: inst.context = null;
249 },
250
251 + /**
252 + * Sets a subset of the props.
253 + *
254 + * @param {object} partialProps Subset of the next props.
255 + * @param {?function} callback Called after props are updated.
256 + * @final
257 + * @public
258 + */
259 + setProps: function(partialProps, callback) {
260 + // Merge with the pending element if it exists, otherwise with existing
261 + // element props.
262 + var element = this._pendingElement || this._currentElement;
263 + this.replaceProps(
264 + assign({}, element.props, partialProps),
265 + callback
266 + );
267 + },
268 +
269 + /**
270 + * Replaces all of the props.
271 + *
272 + * @param {object} props New props.
273 + * @param {?function} callback Called after props are updated.
274 + * @final
275 + * @public
276 + */
277 + replaceProps: function(props, callback) {
278 + invariant(
279 + this._mountDepth === 0,
280 + 'replaceProps(...): You called `setProps` or `replaceProps` on a ' +
281 + 'component with a parent. This is an anti-pattern since props will ' +
282 + 'get reactively updated when rendered. Instead, change the owner\'s ' +
283 + '`render` method to pass the correct value as props to the component ' +
284 + 'where it is created.'
285 + );
286 + // This is a deoptimized path. We optimize for always having an element.
287 + // This creates an extra internal element.
288 + this._pendingElement = ReactElement.cloneAndReplaceProps(
289 + this._pendingElement || this._currentElement,
290 + props
291 + );
292 + ReactUpdates.enqueueUpdate(this, callback);
293 + },
294 +
295 + /**
296 + * Schedule a partial update to the props. Only used for internal testing.
297 + *
298 + * @param {object} partialProps Subset of the next props.
299 + * @param {?function} callback Called after props are updated.
300 + * @final
301 + * @internal
302 + */
303 + _setPropsInternal: function(partialProps, callback) {
304 + // This is a deoptimized path. We optimize for always having an element.
305 + // This creates an extra internal element.
306 + var element = this._pendingElement || this._currentElement;
307 + this._pendingElement = ReactElement.cloneAndReplaceProps(
308 + element,
309 + assign({}, element.props, partialProps)
310 + );
311 + ReactUpdates.enqueueUpdate(this, callback);
312 + },
313 +
314 /**
315 * Sets a subset of the state. This only exists because _pendingState is
316 * internal. This provides a merging strategy that is not available to deep
src/test/ReactDefaultPerf.js
+1 -1
@@ -169,7 +169,7 @@ var ReactDefaultPerf = {
169 rv = func.apply(this, args);
170 totalTime = performanceNow() - start;
171
172 - if (fnName === 'mountImageIntoNode') {
172 + if (fnName === '_mountImageIntoNode') {
173 var mountID = ReactMount.getID(args[1]);
174 ReactDefaultPerf._recordWrite(mountID, fnName, totalTime, args[0]);
175 } else if (fnName === 'dangerouslyProcessChildrenUpdates') {
src/test/ReactDefaultPerfAnalysis.js
+1 -1
@@ -14,7 +14,7 @@ var assign = require('Object.assign');
14 // Don't try to save users less than 1.2ms (a number I made up)
15 var DONT_CARE_THRESHOLD = 1.2;
16 var DOM_OPERATION_TYPES = {
17 - 'mountImageIntoNode': 'set innerHTML',
17 + '_mountImageIntoNode': 'set innerHTML',
18 INSERT_MARKUP: 'set innerHTML',
19 MOVE_EXISTING: 'move',
20 REMOVE_NODE: 'remove',