Convert form, iframe, img to not use wrappers
Test Plan: Tested manually that rendering an `<img />` triggers its onLoad handler.
Ben Alpert committed
Jun 4, 2015 at 15:47 UTC
a825380840c37e9ad5e03a46a8a0389d28dfd0d6
7 files changed
+71
-237
src/renderers/dom/client/wrappers/LocalEventTrapMixin.js
deleted
-54
@@ -1,54 +0,0 @@
1
-/**
2
- * Copyright 2014-2015, 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 LocalEventTrapMixin
10
- */
11
-
12
-'use strict';
13
-
14
-var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
15
-
16
-var accumulateInto = require('accumulateInto');
17
-var findDOMNode = require('findDOMNode');
18
-var forEachAccumulated = require('forEachAccumulated');
19
-var invariant = require('invariant');
20
-
21
-function remove(event) {
22
- event.remove();
23
-}
24
-
25
-var LocalEventTrapMixin = {
26
- trapBubbledEvent(topLevelType, handlerBaseName) {
27
- invariant(this.isMounted(), 'Must be mounted to trap events');
28
- // If a component renders to null or if another component fatals and causes
29
- // the state of the tree to be corrupted, `node` here can be null.
30
- var node = findDOMNode(this);
31
- invariant(
32
- node,
33
- 'LocalEventTrapMixin.trapBubbledEvent(...): Requires node to be rendered.'
34
- );
35
- var listener = ReactBrowserEventEmitter.trapBubbledEvent(
36
- topLevelType,
37
- handlerBaseName,
38
- node
39
- );
40
- this._localEventListeners =
41
- accumulateInto(this._localEventListeners, listener);
42
- },
43
-
44
- // trapCapturedEvent would look nearly identical. We don't implement that
45
- // method because it isn't currently needed.
46
-
47
- componentWillUnmount() {
48
- if (this._localEventListeners) {
49
- forEachAccumulated(this._localEventListeners, remove);
50
- }
51
- },
52
-};
53
-
54
-module.exports = LocalEventTrapMixin;
src/renderers/dom/client/wrappers/ReactDOMForm.js
deleted
-44
@@ -1,44 +0,0 @@
1
-/**
2
- * Copyright 2013-2015, 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 ReactDOMForm
10
- */
11
-
12
-'use strict';
13
-
14
-var EventConstants = require('EventConstants');
15
-var LocalEventTrapMixin = require('LocalEventTrapMixin');
16
-var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
17
-var ReactClass = require('ReactClass');
18
-var ReactElement = require('ReactElement');
19
-
20
-var form = ReactElement.createFactory('form');
21
-
22
-/**
23
- * Since onSubmit doesn't bubble OR capture on the top level in IE8, we need
24
- * to capture it on the <form> element itself. There are lots of hacks we could
25
- * do to accomplish this, but the most reliable is to make <form> a
26
- * composite component and use `componentDidMount` to attach the event handlers.
27
- */
28
-var ReactDOMForm = ReactClass.createClass({
29
- displayName: 'ReactDOMForm',
30
- tagName: 'FORM',
31
-
32
- mixins: [ReactBrowserComponentMixin, LocalEventTrapMixin],
33
-
34
- render: function() {
35
- return form(this.props);
36
- },
37
-
38
- componentDidMount: function() {
39
- this.trapBubbledEvent(EventConstants.topLevelTypes.topReset, 'reset');
40
- this.trapBubbledEvent(EventConstants.topLevelTypes.topSubmit, 'submit');
41
- },
42
-});
43
-
44
-module.exports = ReactDOMForm;
src/renderers/dom/client/wrappers/ReactDOMIframe.js
deleted
-43
@@ -1,43 +0,0 @@
1
-/**
2
- * Copyright 2013-2015, 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 ReactDOMIframe
10
- */
11
-
12
-'use strict';
13
-
14
-var EventConstants = require('EventConstants');
15
-var LocalEventTrapMixin = require('LocalEventTrapMixin');
16
-var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
17
-var ReactClass = require('ReactClass');
18
-var ReactElement = require('ReactElement');
19
-
20
-var iframe = ReactElement.createFactory('iframe');
21
-
22
-/**
23
- * Since onLoad doesn't bubble OR capture on the top level in IE8, we need to
24
- * capture it on the <iframe> element itself. There are lots of hacks we could
25
- * do to accomplish this, but the most reliable is to make <iframe> a composite
26
- * component and use `componentDidMount` to attach the event handlers.
27
- */
28
-var ReactDOMIframe = ReactClass.createClass({
29
- displayName: 'ReactDOMIframe',
30
- tagName: 'IFRAME',
31
-
32
- mixins: [ReactBrowserComponentMixin, LocalEventTrapMixin],
33
-
34
- render: function() {
35
- return iframe(this.props);
36
- },
37
-
38
- componentDidMount: function() {
39
- this.trapBubbledEvent(EventConstants.topLevelTypes.topLoad, 'load');
40
- },
41
-});
42
-
43
-module.exports = ReactDOMIframe;
src/renderers/dom/client/wrappers/ReactDOMImg.js
deleted
-44
@@ -1,44 +0,0 @@
1
-/**
2
- * Copyright 2013-2015, 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 ReactDOMImg
10
- */
11
-
12
-'use strict';
13
-
14
-var EventConstants = require('EventConstants');
15
-var LocalEventTrapMixin = require('LocalEventTrapMixin');
16
-var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
17
-var ReactClass = require('ReactClass');
18
-var ReactElement = require('ReactElement');
19
-
20
-var img = ReactElement.createFactory('img');
21
-
22
-/**
23
- * Since onLoad doesn't bubble OR capture on the top level in IE8, we need to
24
- * capture it on the <img> element itself. There are lots of hacks we could do
25
- * to accomplish this, but the most reliable is to make <img> a composite
26
- * component and use `componentDidMount` to attach the event handlers.
27
- */
28
-var ReactDOMImg = ReactClass.createClass({
29
- displayName: 'ReactDOMImg',
30
- tagName: 'IMG',
31
-
32
- mixins: [ReactBrowserComponentMixin, LocalEventTrapMixin],
33
-
34
- render: function() {
35
- return img(this.props);
36
- },
37
-
38
- componentDidMount: function() {
39
- this.trapBubbledEvent(EventConstants.topLevelTypes.topLoad, 'load');
40
- this.trapBubbledEvent(EventConstants.topLevelTypes.topError, 'error');
41
- },
42
-});
43
-
44
-module.exports = ReactDOMImg;
src/renderers/dom/client/wrappers/__tests__/LocalEventTrapMixin-test.js
deleted
-46
@@ -1,46 +0,0 @@
1
-/**
2
- * Copyright 2013-2015, 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
- * @emails react-core
10
- */
11
-
12
-'use strict';
13
-
14
-describe('LocalEventTrapMixin', function() {
15
- var EventConstants;
16
- var LocalEventTrapMixin;
17
- var React;
18
- var ReactTestUtils;
19
-
20
- beforeEach(function() {
21
- EventConstants = require('EventConstants');
22
- LocalEventTrapMixin = require('LocalEventTrapMixin');
23
- React = require('React');
24
- ReactTestUtils = require('ReactTestUtils');
25
- });
26
-
27
- it('throws when trapping bubbled state on null', function() {
28
- var BadImage = React.createClass({
29
- mixins: [LocalEventTrapMixin],
30
- render: function() {
31
- return null;
32
- },
33
- componentDidMount: function() {
34
- this.trapBubbledEvent(EventConstants.topLevelTypes.topLoad, 'load');
35
- },
36
- });
37
-
38
- expect(function() {
39
- ReactTestUtils.renderIntoDocument(<BadImage />);
40
- }).toThrow(
41
- 'Invariant Violation: ' +
42
- 'LocalEventTrapMixin.trapBubbledEvent(...): ' +
43
- 'Requires node to be rendered.'
44
- );
45
- });
46
-});
src/renderers/dom/shared/ReactDOMComponent.js
+71
@@ -18,6 +18,7 @@ var AutoFocusUtils = require('AutoFocusUtils');
18
var CSSPropertyOperations = require('CSSPropertyOperations');
19
var DOMProperty = require('DOMProperty');
20
var DOMPropertyOperations = require('DOMPropertyOperations');
21
+var EventConstants = require('EventConstants');
22
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
23
var ReactComponentBrowserEnvironment =
24
require('ReactComponentBrowserEnvironment');
@@ -176,6 +177,58 @@ function putListener() {
177
);
178
}
179
180
+function trapBubbledEventsLocal() {
181
+ var inst = this;
182
+ // If a component renders to null or if another component fatals and causes
183
+ // the state of the tree to be corrupted, `node` here can be null.
184
+ invariant(inst._rootNodeID, 'Must be mounted to trap events');
185
+ var node = ReactMount.getNode(inst._rootNodeID);
186
+ invariant(
187
+ node,
188
+ 'trapBubbledEvent(...): Requires node to be rendered.'
189
+ );
190
+
191
+ switch (inst._tag) {
192
+ case 'iframe':
193
+ inst._wrapperState.listeners = [
194
+ ReactBrowserEventEmitter.trapBubbledEvent(
195
+ EventConstants.topLevelTypes.topLoad,
196
+ 'load',
197
+ node
198
+ ),
199
+ ];
200
+ break;
201
+ case 'img':
202
+ inst._wrapperState.listeners = [
203
+ ReactBrowserEventEmitter.trapBubbledEvent(
204
+ EventConstants.topLevelTypes.topError,
205
+ 'error',
206
+ node
207
+ ),
208
+ ReactBrowserEventEmitter.trapBubbledEvent(
209
+ EventConstants.topLevelTypes.topLoad,
210
+ 'load',
211
+ node
212
+ ),
213
+ ];
214
+ break;
215
+ case 'form':
216
+ inst._wrapperState.listeners = [
217
+ ReactBrowserEventEmitter.trapBubbledEvent(
218
+ EventConstants.topLevelTypes.topReset,
219
+ 'reset',
220
+ node
221
+ ),
222
+ ReactBrowserEventEmitter.trapBubbledEvent(
223
+ EventConstants.topLevelTypes.topSubmit,
224
+ 'submit',
225
+ node
226
+ ),
227
+ ];
228
+ break;
229
+ }
230
+}
231
+
232
// For HTML, certain tags should omit their close tag. We keep a whitelist for
233
// those special cased tags.
234
@@ -285,6 +338,14 @@ ReactDOMComponent.Mixin = {
338
var props = this._currentElement.props;
339
340
switch (this._tag) {
341
+ case 'iframe':
342
+ case 'img':
343
+ case 'form':
344
+ this._wrapperState = {
345
+ listeners: null,
346
+ };
347
+ transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
348
+ break;
349
case 'button':
350
props = ReactDOMButton.getNativeProps(this, props, context);
351
break;
@@ -679,6 +740,16 @@ ReactDOMComponent.Mixin = {
740
*/
741
unmountComponent: function() {
742
switch (this._tag) {
743
+ case 'iframe':
744
+ case 'img':
745
+ case 'form':
746
+ var listeners = this._wrapperState.listeners;
747
+ if (listeners) {
748
+ for (var i = 0; i < listeners.length; i++) {
749
+ listeners[i].remove();
750
+ }
751
+ }
752
+ break;
753
case 'input':
754
ReactDOMInput.unmountWrapper(this);
755
break;
src/renderers/dom/shared/ReactDefaultInjection.js
-6
@@ -24,10 +24,7 @@ var ReactComponentBrowserEnvironment =
24
require('ReactComponentBrowserEnvironment');
25
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
26
var ReactDOMComponent = require('ReactDOMComponent');
27
-var ReactDOMForm = require('ReactDOMForm');
28
-var ReactDOMImg = require('ReactDOMImg');
27
var ReactDOMIDOperations = require('ReactDOMIDOperations');
30
-var ReactDOMIframe = require('ReactDOMIframe');
28
var ReactDOMOption = require('ReactDOMOption');
29
var ReactDOMSelect = require('ReactDOMSelect');
30
var ReactDOMTextComponent = require('ReactDOMTextComponent');
@@ -109,9 +106,6 @@ function inject() {
106
ReactInjection.Class.injectMixin(ReactBrowserComponentMixin);
107
108
ReactInjection.NativeComponent.injectComponentClasses({
112
- 'form': ReactDOMForm,
113
- 'iframe': ReactDOMIframe,
114
- 'img': ReactDOMImg,
109
'option': ReactDOMOption,
110
'select': ReactDOMSelect,
111
});