Break more browser deps in core
This is part of what Sebastian suggested. We require it into every component that explicitly needs it and inject it as a default mixin for composite components in the browser environment. This change frees us up to inject everything in ReactComponent.
Pete Hunt committed
Feb 20, 2014 at 16:00 UTC
b79a3cbd21a82a44ac0c8863995a5f12b10a67d6
15 files changed
+115
-58
src/browser/ReactBrowserComponentMixin.js
renamed
+21
-5
@@ -13,14 +13,30 @@
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
15
*
16
- * @providesModule ReactComponentEnvironment
16
+ * @providesModule ReactBrowserComponentMixin
17
*/
18
19
"use strict";
20
21
-var ReactComponentBrowserEnvironment =
22
- require('ReactComponentBrowserEnvironment');
21
+var ReactMount = require('ReactMount');
22
24
-var ReactComponentEnvironment = ReactComponentBrowserEnvironment;
23
+var invariant = require('invariant');
24
26
-module.exports = ReactComponentEnvironment;
25
+var ReactBrowserComponentMixin = {
26
+ /**
27
+ * Returns the DOM node rendered by this component.
28
+ *
29
+ * @return {DOMElement} The root node of this component.
30
+ * @final
31
+ * @protected
32
+ */
33
+ getDOMNode: function() {
34
+ invariant(
35
+ this.isMounted(),
36
+ 'getDOMNode(): A component must be mounted to have a DOM node.'
37
+ );
38
+ return ReactMount.getNode(this._rootNodeID);
39
+ }
40
+};
41
+
42
+module.exports = ReactBrowserComponentMixin;
src/browser/ReactComponentBrowserEnvironment.js
-20
@@ -39,26 +39,6 @@ var DOC_NODE_TYPE = 9;
39
* the browser context.
40
*/
41
var ReactComponentBrowserEnvironment = {
42
- /**
43
- * Mixed into every component instance.
44
- */
45
- Mixin: {
46
- /**
47
- * Returns the DOM node rendered by this component.
48
- *
49
- * @return {DOMElement} The root node of this component.
50
- * @final
51
- * @protected
52
- */
53
- getDOMNode: function() {
54
- invariant(
55
- this.isMounted(),
56
- 'getDOMNode(): A component must be mounted to have a DOM node.'
57
- );
58
- return ReactMount.getNode(this._rootNodeID);
59
- }
60
- },
61
-
42
ReactReconcileTransaction: ReactReconcileTransaction,
43
44
BackendIDOperations: ReactDOMIDOperations,
src/browser/ReactDOMComponent.js
+2
@@ -22,6 +22,7 @@
22
var CSSPropertyOperations = require('CSSPropertyOperations');
23
var DOMProperty = require('DOMProperty');
24
var DOMPropertyOperations = require('DOMPropertyOperations');
25
+var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
26
var ReactComponent = require('ReactComponent');
27
var ReactEventEmitter = require('ReactEventEmitter');
28
var ReactMount = require('ReactMount');
@@ -399,5 +400,6 @@ ReactDOMComponent.Mixin = {
400
mixInto(ReactDOMComponent, ReactComponent.Mixin);
401
mixInto(ReactDOMComponent, ReactDOMComponent.Mixin);
402
mixInto(ReactDOMComponent, ReactMultiChild.Mixin);
403
+mixInto(ReactDOMComponent, ReactBrowserComponentMixin);
404
405
module.exports = ReactDOMComponent;
src/browser/ReactDefaultInjection.js
+10
@@ -30,6 +30,9 @@ var CompositionEventPlugin = require('CompositionEventPlugin');
30
var DefaultEventPluginOrder = require('DefaultEventPluginOrder');
31
var EnterLeaveEventPlugin = require('EnterLeaveEventPlugin');
32
var MobileSafariClickEventPlugin = require('MobileSafariClickEventPlugin');
33
+var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
34
+var ReactComponentBrowserEnvironment =
35
+ require('ReactComponentBrowserEnvironment');
36
var ReactEventTopLevelCallback = require('ReactEventTopLevelCallback');
37
var ReactDOM = require('ReactDOM');
38
var ReactDOMButton = require('ReactDOMButton');
@@ -89,6 +92,11 @@ function inject() {
92
body: createFullPageComponent(ReactDOM.body)
93
});
94
95
+
96
+ // This needs to happen after createFullPageComponent() otherwise the mixin
97
+ // gets double injected.
98
+ ReactInjection.CompositeComponent.injectMixin(ReactBrowserComponentMixin);
99
+
100
ReactInjection.DOMProperty.injectDOMPropertyConfig(DefaultDOMPropertyConfig);
101
102
ReactInjection.Updates.injectBatchingStrategy(
@@ -101,6 +109,8 @@ function inject() {
109
ServerReactRootIndex.createReactRootIndex
110
);
111
112
+ ReactInjection.Component.injectEnvironment(ReactComponentBrowserEnvironment);
113
+
114
if (__DEV__) {
115
var url = (ExecutionEnvironment.canUseDOM && window.location.href) || '';
116
if ((/[?&]react_perf\b/).test(url)) {
src/browser/ReactTextComponent.js
+2
@@ -20,6 +20,7 @@
20
"use strict";
21
22
var DOMPropertyOperations = require('DOMPropertyOperations');
23
+var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
24
var ReactComponent = require('ReactComponent');
25
26
var escapeTextForBrowser = require('escapeTextForBrowser');
@@ -45,6 +46,7 @@ var ReactTextComponent = function(initialText) {
46
};
47
48
mixInto(ReactTextComponent, ReactComponent.Mixin);
49
+mixInto(ReactTextComponent, ReactBrowserComponentMixin);
50
mixInto(ReactTextComponent, {
51
52
/**
src/browser/dom/components/ReactDOMButton.js
+2
-1
@@ -19,6 +19,7 @@
19
"use strict";
20
21
var AutoFocusMixin = require('AutoFocusMixin');
22
+var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
23
var ReactCompositeComponent = require('ReactCompositeComponent');
24
var ReactDOM = require('ReactDOM');
25
@@ -47,7 +48,7 @@ var mouseListenerNames = keyMirror({
48
var ReactDOMButton = ReactCompositeComponent.createClass({
49
displayName: 'ReactDOMButton',
50
50
- mixins: [AutoFocusMixin],
51
+ mixins: [AutoFocusMixin, ReactBrowserComponentMixin],
52
53
render: function() {
54
var props = {};
src/browser/dom/components/ReactDOMForm.js
+3
@@ -18,6 +18,7 @@
18
19
"use strict";
20
21
+var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
22
var ReactCompositeComponent = require('ReactCompositeComponent');
23
var ReactDOM = require('ReactDOM');
24
var ReactEventEmitter = require('ReactEventEmitter');
@@ -35,6 +36,8 @@ var form = ReactDOM.form;
36
var ReactDOMForm = ReactCompositeComponent.createClass({
37
displayName: 'ReactDOMForm',
38
39
+ mixins: [ReactBrowserComponentMixin],
40
+
41
render: function() {
42
// TODO: Instead of using `ReactDOM` directly, we should use JSX. However,
43
// `jshint` fails to parse JSX so in order for linting to work in the open
src/browser/dom/components/ReactDOMImg.js
+3
@@ -18,6 +18,7 @@
18
19
"use strict";
20
21
+var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
22
var ReactCompositeComponent = require('ReactCompositeComponent');
23
var ReactDOM = require('ReactDOM');
24
var ReactEventEmitter = require('ReactEventEmitter');
@@ -36,6 +37,8 @@ var ReactDOMImg = ReactCompositeComponent.createClass({
37
displayName: 'ReactDOMImg',
38
tagName: 'IMG',
39
40
+ mixins: [ReactBrowserComponentMixin],
41
+
42
render: function() {
43
return img(this.props);
44
},
src/browser/dom/components/ReactDOMInput.js
+2
-1
@@ -21,6 +21,7 @@
21
var AutoFocusMixin = require('AutoFocusMixin');
22
var DOMPropertyOperations = require('DOMPropertyOperations');
23
var LinkedValueUtils = require('LinkedValueUtils');
24
+var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
25
var ReactCompositeComponent = require('ReactCompositeComponent');
26
var ReactDOM = require('ReactDOM');
27
var ReactMount = require('ReactMount');
@@ -52,7 +53,7 @@ var instancesByReactID = {};
53
var ReactDOMInput = ReactCompositeComponent.createClass({
54
displayName: 'ReactDOMInput',
55
55
- mixins: [AutoFocusMixin, LinkedValueUtils.Mixin],
56
+ mixins: [AutoFocusMixin, LinkedValueUtils.Mixin, ReactBrowserComponentMixin],
57
58
getInitialState: function() {
59
var defaultValue = this.props.defaultValue;
src/browser/dom/components/ReactDOMOption.js
+3
@@ -18,6 +18,7 @@
18
19
"use strict";
20
21
+var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
22
var ReactCompositeComponent = require('ReactCompositeComponent');
23
var ReactDOM = require('ReactDOM');
24
@@ -30,6 +31,8 @@ var option = ReactDOM.option;
31
var ReactDOMOption = ReactCompositeComponent.createClass({
32
displayName: 'ReactDOMOption',
33
34
+ mixins: [ReactBrowserComponentMixin],
35
+
36
componentWillMount: function() {
37
// TODO (yungsters): Remove support for `selected` in <option>.
38
if (this.props.selected != null) {
src/browser/dom/components/ReactDOMSelect.js
+2
-1
@@ -20,6 +20,7 @@
20
21
var AutoFocusMixin = require('AutoFocusMixin');
22
var LinkedValueUtils = require('LinkedValueUtils');
23
+var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
24
var ReactCompositeComponent = require('ReactCompositeComponent');
25
var ReactDOM = require('ReactDOM');
26
@@ -103,7 +104,7 @@ function updateOptions(component, propValue) {
104
var ReactDOMSelect = ReactCompositeComponent.createClass({
105
displayName: 'ReactDOMSelect',
106
106
- mixins: [AutoFocusMixin, LinkedValueUtils.Mixin],
107
+ mixins: [AutoFocusMixin, LinkedValueUtils.Mixin, ReactBrowserComponentMixin],
108
109
propTypes: {
110
defaultValue: selectValueType,
src/browser/dom/components/ReactDOMTextarea.js
+2
-1
@@ -21,6 +21,7 @@
21
var AutoFocusMixin = require('AutoFocusMixin');
22
var DOMPropertyOperations = require('DOMPropertyOperations');
23
var LinkedValueUtils = require('LinkedValueUtils');
24
+var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
25
var ReactCompositeComponent = require('ReactCompositeComponent');
26
var ReactDOM = require('ReactDOM');
27
@@ -48,7 +49,7 @@ var textarea = ReactDOM.textarea;
49
var ReactDOMTextarea = ReactCompositeComponent.createClass({
50
displayName: 'ReactDOMTextarea',
51
51
- mixins: [AutoFocusMixin, LinkedValueUtils.Mixin],
52
+ mixins: [AutoFocusMixin, LinkedValueUtils.Mixin, ReactBrowserComponentMixin],
53
54
getInitialState: function() {
55
var defaultValue = this.props.defaultValue;
src/core/ReactComponent.js
+45
-28
@@ -18,7 +18,6 @@
18
19
"use strict";
20
21
-var ReactComponentEnvironment = require('ReactComponentEnvironment');
21
var ReactCurrentOwner = require('ReactCurrentOwner');
22
var ReactOwner = require('ReactOwner');
23
var ReactUpdates = require('ReactUpdates');
@@ -53,6 +52,28 @@ var ownerHasPropertyWarning = {};
52
53
var NUMERIC_PROPERTY_REGEX = /^\d+$/;
54
55
+var injected = false;
56
+
57
+/**
58
+ * Optionally injectable environment dependent cleanup hook. (server vs.
59
+ * browser etc). Example: A browser system caches DOM nodes based on component
60
+ * ID and must remove that cache entry when this instance is unmounted.
61
+ *
62
+ * @private
63
+ */
64
+var unmountIDFromEnvironment = null;
65
+
66
+/**
67
+ * The "image" of a component tree, is the platform specific (typically
68
+ * serialized) data that represents a tree of lower level UI building blocks.
69
+ * On the web, this "image" is HTML markup which describes a construction of
70
+ * low level `div` and `span` nodes. Other platforms may have different
71
+ * encoding of this "image". This must be injected.
72
+ *
73
+ * @private
74
+ */
75
+var mountImageIntoNode = null;
76
+
77
/**
78
* Warn if the component doesn't have an explicit key assigned to it.
79
* This component is in an array. The array could grow and shrink or be
@@ -177,6 +198,23 @@ function validateChildKeys(component) {
198
*/
199
var ReactComponent = {
200
201
+ injection: {
202
+ injectEnvironment: function(ReactComponentEnvironment) {
203
+ invariant(
204
+ !injected,
205
+ 'ReactComponent: injectEnvironment() can only be called once.'
206
+ );
207
+ mountImageIntoNode = ReactComponentEnvironment.mountImageIntoNode;
208
+ unmountIDFromEnvironment =
209
+ ReactComponentEnvironment.unmountIDFromEnvironment;
210
+ ReactComponent.BackendIDOperations =
211
+ ReactComponentEnvironment.BackendIDOperations;
212
+ ReactComponent.ReactReconcileTransaction =
213
+ ReactComponentEnvironment.ReactReconcileTransaction;
214
+ injected = true;
215
+ }
216
+ },
217
+
218
/**
219
* @param {?object} object
220
* @return {boolean} True if `object` is a valid component.
@@ -209,27 +247,7 @@ var ReactComponent = {
247
*
248
* @internal
249
*/
212
- BackendIDOperations: ReactComponentEnvironment.BackendIDOperations,
213
-
214
- /**
215
- * Optionally injectable environment dependent cleanup hook. (server vs.
216
- * browser etc). Example: A browser system caches DOM nodes based on component
217
- * ID and must remove that cache entry when this instance is unmounted.
218
- *
219
- * @private
220
- */
221
- unmountIDFromEnvironment: ReactComponentEnvironment.unmountIDFromEnvironment,
222
-
223
- /**
224
- * The "image" of a component tree, is the platform specific (typically
225
- * serialized) data that represents a tree of lower level UI building blocks.
226
- * On the web, this "image" is HTML markup which describes a construction of
227
- * low level `div` and `span` nodes. Other platforms may have different
228
- * encoding of this "image". This must be injected.
229
- *
230
- * @private
231
- */
232
- mountImageIntoNode: ReactComponentEnvironment.mountImageIntoNode,
250
+ BackendIDOperations: null,
251
252
/**
253
* React references `ReactReconcileTransaction` using this property in order
@@ -237,8 +255,7 @@ var ReactComponent = {
255
*
256
* @internal
257
*/
240
- ReactReconcileTransaction:
241
- ReactComponentEnvironment.ReactReconcileTransaction,
258
+ ReactReconcileTransaction: null,
259
260
/**
261
* Base functionality for every ReactComponent constructor. Mixed into the
@@ -246,7 +263,7 @@ var ReactComponent = {
263
*
264
* @lends {ReactComponent.prototype}
265
*/
249
- Mixin: merge(ReactComponentEnvironment.Mixin, {
266
+ Mixin: {
267
268
/**
269
* Checks whether or not this component is mounted.
@@ -396,7 +413,7 @@ var ReactComponent = {
413
if (props.ref != null) {
414
ReactOwner.removeComponentAsRefFrom(this, props.ref, this._owner);
415
}
399
- ReactComponent.unmountIDFromEnvironment(this._rootNodeID);
416
+ unmountIDFromEnvironment(this._rootNodeID);
417
this._rootNodeID = null;
418
this._lifeCycleState = ComponentLifeCycle.UNMOUNTED;
419
},
@@ -513,7 +530,7 @@ var ReactComponent = {
530
transaction,
531
shouldReuseMarkup) {
532
var markup = this.mountComponent(rootID, transaction, 0);
516
- ReactComponent.mountImageIntoNode(markup, container, shouldReuseMarkup);
533
+ mountImageIntoNode(markup, container, shouldReuseMarkup);
534
},
535
536
/**
@@ -543,7 +560,7 @@ var ReactComponent = {
560
}
561
return owner.refs[ref];
562
}
546
- })
563
+ }
564
};
565
566
module.exports = ReactComponent;
src/core/ReactCompositeComponent.js
+14
-1
@@ -61,6 +61,9 @@ var SpecPolicy = keyMirror({
61
DEFINE_MANY_MERGED: null
62
});
63
64
+
65
+var injectedMixins = [];
66
+
67
/**
68
* Composite components are higher-level components that compose other composite
69
* or native components.
@@ -1405,6 +1408,10 @@ var ReactCompositeComponent = {
1408
Constructor.ConvenienceConstructor = ConvenienceConstructor;
1409
ConvenienceConstructor.originalSpec = spec;
1410
1411
+ injectedMixins.forEach(
1412
+ mixSpecIntoComponent.bind(null, ConvenienceConstructor)
1413
+ );
1414
+
1415
mixSpecIntoComponent(ConvenienceConstructor, spec);
1416
1417
invariant(
@@ -1445,7 +1452,13 @@ var ReactCompositeComponent = {
1452
return ConvenienceConstructor;
1453
},
1454
1448
- isValidClass: isValidClass
1455
+ isValidClass: isValidClass,
1456
+
1457
+ injection: {
1458
+ injectMixin: function(mixin) {
1459
+ injectedMixins.push(mixin);
1460
+ }
1461
+ }
1462
};
1463
1464
module.exports = ReactCompositeComponent;
src/core/ReactInjection.js
+4
@@ -20,6 +20,8 @@
20
21
var DOMProperty = require('DOMProperty');
22
var EventPluginHub = require('EventPluginHub');
23
+var ReactComponent = require('ReactComponent');
24
+var ReactCompositeComponent = require('ReactCompositeComponent');
25
var ReactDOM = require('ReactDOM');
26
var ReactEventEmitter = require('ReactEventEmitter');
27
var ReactPerf = require('ReactPerf');
@@ -27,6 +29,8 @@ var ReactRootIndex = require('ReactRootIndex');
29
var ReactUpdates = require('ReactUpdates');
30
31
var ReactInjection = {
32
+ Component: ReactComponent.injection,
33
+ CompositeComponent: ReactCompositeComponent.injection,
34
DOMProperty: DOMProperty.injection,
35
EventPluginHub: EventPluginHub.injection,
36
DOM: ReactDOM.injection,