mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Merge pull request #4033 from spicyj/dc-events
Convert form, iframe, img to not use wrappers
This commit is contained in:
@@ -1,54 +0,0 @@
|
||||
/**
|
||||
* Copyright 2014-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule LocalEventTrapMixin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
|
||||
|
||||
var accumulateInto = require('accumulateInto');
|
||||
var findDOMNode = require('findDOMNode');
|
||||
var forEachAccumulated = require('forEachAccumulated');
|
||||
var invariant = require('invariant');
|
||||
|
||||
function remove(event) {
|
||||
event.remove();
|
||||
}
|
||||
|
||||
var LocalEventTrapMixin = {
|
||||
trapBubbledEvent(topLevelType, handlerBaseName) {
|
||||
invariant(this.isMounted(), 'Must be mounted to trap events');
|
||||
// If a component renders to null or if another component fatals and causes
|
||||
// the state of the tree to be corrupted, `node` here can be null.
|
||||
var node = findDOMNode(this);
|
||||
invariant(
|
||||
node,
|
||||
'LocalEventTrapMixin.trapBubbledEvent(...): Requires node to be rendered.'
|
||||
);
|
||||
var listener = ReactBrowserEventEmitter.trapBubbledEvent(
|
||||
topLevelType,
|
||||
handlerBaseName,
|
||||
node
|
||||
);
|
||||
this._localEventListeners =
|
||||
accumulateInto(this._localEventListeners, listener);
|
||||
},
|
||||
|
||||
// trapCapturedEvent would look nearly identical. We don't implement that
|
||||
// method because it isn't currently needed.
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._localEventListeners) {
|
||||
forEachAccumulated(this._localEventListeners, remove);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = LocalEventTrapMixin;
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactDOMForm
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var EventConstants = require('EventConstants');
|
||||
var LocalEventTrapMixin = require('LocalEventTrapMixin');
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
|
||||
var form = ReactElement.createFactory('form');
|
||||
|
||||
/**
|
||||
* Since onSubmit doesn't bubble OR capture on the top level in IE8, we need
|
||||
* to capture it on the <form> element itself. There are lots of hacks we could
|
||||
* do to accomplish this, but the most reliable is to make <form> a
|
||||
* composite component and use `componentDidMount` to attach the event handlers.
|
||||
*/
|
||||
var ReactDOMForm = ReactClass.createClass({
|
||||
displayName: 'ReactDOMForm',
|
||||
tagName: 'FORM',
|
||||
|
||||
mixins: [ReactBrowserComponentMixin, LocalEventTrapMixin],
|
||||
|
||||
render: function() {
|
||||
return form(this.props);
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.trapBubbledEvent(EventConstants.topLevelTypes.topReset, 'reset');
|
||||
this.trapBubbledEvent(EventConstants.topLevelTypes.topSubmit, 'submit');
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ReactDOMForm;
|
||||
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactDOMIframe
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var EventConstants = require('EventConstants');
|
||||
var LocalEventTrapMixin = require('LocalEventTrapMixin');
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
|
||||
var iframe = ReactElement.createFactory('iframe');
|
||||
|
||||
/**
|
||||
* Since onLoad doesn't bubble OR capture on the top level in IE8, we need to
|
||||
* capture it on the <iframe> element itself. There are lots of hacks we could
|
||||
* do to accomplish this, but the most reliable is to make <iframe> a composite
|
||||
* component and use `componentDidMount` to attach the event handlers.
|
||||
*/
|
||||
var ReactDOMIframe = ReactClass.createClass({
|
||||
displayName: 'ReactDOMIframe',
|
||||
tagName: 'IFRAME',
|
||||
|
||||
mixins: [ReactBrowserComponentMixin, LocalEventTrapMixin],
|
||||
|
||||
render: function() {
|
||||
return iframe(this.props);
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.trapBubbledEvent(EventConstants.topLevelTypes.topLoad, 'load');
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ReactDOMIframe;
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactDOMImg
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var EventConstants = require('EventConstants');
|
||||
var LocalEventTrapMixin = require('LocalEventTrapMixin');
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
|
||||
var img = ReactElement.createFactory('img');
|
||||
|
||||
/**
|
||||
* Since onLoad doesn't bubble OR capture on the top level in IE8, we need to
|
||||
* capture it on the <img> element itself. There are lots of hacks we could do
|
||||
* to accomplish this, but the most reliable is to make <img> a composite
|
||||
* component and use `componentDidMount` to attach the event handlers.
|
||||
*/
|
||||
var ReactDOMImg = ReactClass.createClass({
|
||||
displayName: 'ReactDOMImg',
|
||||
tagName: 'IMG',
|
||||
|
||||
mixins: [ReactBrowserComponentMixin, LocalEventTrapMixin],
|
||||
|
||||
render: function() {
|
||||
return img(this.props);
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.trapBubbledEvent(EventConstants.topLevelTypes.topLoad, 'load');
|
||||
this.trapBubbledEvent(EventConstants.topLevelTypes.topError, 'error');
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ReactDOMImg;
|
||||
@@ -1,46 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('LocalEventTrapMixin', function() {
|
||||
var EventConstants;
|
||||
var LocalEventTrapMixin;
|
||||
var React;
|
||||
var ReactTestUtils;
|
||||
|
||||
beforeEach(function() {
|
||||
EventConstants = require('EventConstants');
|
||||
LocalEventTrapMixin = require('LocalEventTrapMixin');
|
||||
React = require('React');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
});
|
||||
|
||||
it('throws when trapping bubbled state on null', function() {
|
||||
var BadImage = React.createClass({
|
||||
mixins: [LocalEventTrapMixin],
|
||||
render: function() {
|
||||
return null;
|
||||
},
|
||||
componentDidMount: function() {
|
||||
this.trapBubbledEvent(EventConstants.topLevelTypes.topLoad, 'load');
|
||||
},
|
||||
});
|
||||
|
||||
expect(function() {
|
||||
ReactTestUtils.renderIntoDocument(<BadImage />);
|
||||
}).toThrow(
|
||||
'Invariant Violation: ' +
|
||||
'LocalEventTrapMixin.trapBubbledEvent(...): ' +
|
||||
'Requires node to be rendered.'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -18,6 +18,7 @@ var AutoFocusUtils = require('AutoFocusUtils');
|
||||
var CSSPropertyOperations = require('CSSPropertyOperations');
|
||||
var DOMProperty = require('DOMProperty');
|
||||
var DOMPropertyOperations = require('DOMPropertyOperations');
|
||||
var EventConstants = require('EventConstants');
|
||||
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
|
||||
var ReactComponentBrowserEnvironment =
|
||||
require('ReactComponentBrowserEnvironment');
|
||||
@@ -176,6 +177,58 @@ function putListener() {
|
||||
);
|
||||
}
|
||||
|
||||
function trapBubbledEventsLocal() {
|
||||
var inst = this;
|
||||
// If a component renders to null or if another component fatals and causes
|
||||
// the state of the tree to be corrupted, `node` here can be null.
|
||||
invariant(inst._rootNodeID, 'Must be mounted to trap events');
|
||||
var node = ReactMount.getNode(inst._rootNodeID);
|
||||
invariant(
|
||||
node,
|
||||
'trapBubbledEvent(...): Requires node to be rendered.'
|
||||
);
|
||||
|
||||
switch (inst._tag) {
|
||||
case 'iframe':
|
||||
inst._wrapperState.listeners = [
|
||||
ReactBrowserEventEmitter.trapBubbledEvent(
|
||||
EventConstants.topLevelTypes.topLoad,
|
||||
'load',
|
||||
node
|
||||
),
|
||||
];
|
||||
break;
|
||||
case 'img':
|
||||
inst._wrapperState.listeners = [
|
||||
ReactBrowserEventEmitter.trapBubbledEvent(
|
||||
EventConstants.topLevelTypes.topError,
|
||||
'error',
|
||||
node
|
||||
),
|
||||
ReactBrowserEventEmitter.trapBubbledEvent(
|
||||
EventConstants.topLevelTypes.topLoad,
|
||||
'load',
|
||||
node
|
||||
),
|
||||
];
|
||||
break;
|
||||
case 'form':
|
||||
inst._wrapperState.listeners = [
|
||||
ReactBrowserEventEmitter.trapBubbledEvent(
|
||||
EventConstants.topLevelTypes.topReset,
|
||||
'reset',
|
||||
node
|
||||
),
|
||||
ReactBrowserEventEmitter.trapBubbledEvent(
|
||||
EventConstants.topLevelTypes.topSubmit,
|
||||
'submit',
|
||||
node
|
||||
),
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// For HTML, certain tags should omit their close tag. We keep a whitelist for
|
||||
// those special cased tags.
|
||||
|
||||
@@ -285,6 +338,14 @@ ReactDOMComponent.Mixin = {
|
||||
var props = this._currentElement.props;
|
||||
|
||||
switch (this._tag) {
|
||||
case 'iframe':
|
||||
case 'img':
|
||||
case 'form':
|
||||
this._wrapperState = {
|
||||
listeners: null,
|
||||
};
|
||||
transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
|
||||
break;
|
||||
case 'button':
|
||||
props = ReactDOMButton.getNativeProps(this, props, context);
|
||||
break;
|
||||
@@ -679,6 +740,16 @@ ReactDOMComponent.Mixin = {
|
||||
*/
|
||||
unmountComponent: function() {
|
||||
switch (this._tag) {
|
||||
case 'iframe':
|
||||
case 'img':
|
||||
case 'form':
|
||||
var listeners = this._wrapperState.listeners;
|
||||
if (listeners) {
|
||||
for (var i = 0; i < listeners.length; i++) {
|
||||
listeners[i].remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'input':
|
||||
ReactDOMInput.unmountWrapper(this);
|
||||
break;
|
||||
|
||||
@@ -24,10 +24,7 @@ var ReactComponentBrowserEnvironment =
|
||||
require('ReactComponentBrowserEnvironment');
|
||||
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
|
||||
var ReactDOMComponent = require('ReactDOMComponent');
|
||||
var ReactDOMForm = require('ReactDOMForm');
|
||||
var ReactDOMImg = require('ReactDOMImg');
|
||||
var ReactDOMIDOperations = require('ReactDOMIDOperations');
|
||||
var ReactDOMIframe = require('ReactDOMIframe');
|
||||
var ReactDOMOption = require('ReactDOMOption');
|
||||
var ReactDOMSelect = require('ReactDOMSelect');
|
||||
var ReactDOMTextComponent = require('ReactDOMTextComponent');
|
||||
@@ -109,9 +106,6 @@ function inject() {
|
||||
ReactInjection.Class.injectMixin(ReactBrowserComponentMixin);
|
||||
|
||||
ReactInjection.NativeComponent.injectComponentClasses({
|
||||
'form': ReactDOMForm,
|
||||
'iframe': ReactDOMIframe,
|
||||
'img': ReactDOMImg,
|
||||
'option': ReactDOMOption,
|
||||
'select': ReactDOMSelect,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user