@samitouri / QOS-React-2 / commits / a825380840

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 });