diff --git a/src/browser/ReactEventEmitter.js b/src/browser/ReactEventEmitter.js index 5f1956d7d9db4..26cbfdfee3698 100644 --- a/src/browser/ReactEventEmitter.js +++ b/src/browser/ReactEventEmitter.js @@ -144,10 +144,12 @@ function getListeningForDocument(mountAt) { * @param {string} topLevelType Record from `EventConstants`. * @param {string} handlerBaseName Event name (e.g. "click"). * @param {DOMEventTarget} element Element on which to attach listener. + * @return {object} An object with a remove function which will forcefully + * remove the listener. * @internal */ function trapBubbledEvent(topLevelType, handlerBaseName, element) { - EventListener.listen( + return EventListener.listen( element, handlerBaseName, ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback( @@ -162,10 +164,12 @@ function trapBubbledEvent(topLevelType, handlerBaseName, element) { * @param {string} topLevelType Record from `EventConstants`. * @param {string} handlerBaseName Event name (e.g. "click"). * @param {DOMEventTarget} element Element on which to attach listener. + * @return {object} An object with a remove function which will forcefully + * remove the listener. * @internal */ function trapCapturedEvent(topLevelType, handlerBaseName, element) { - EventListener.capture( + return EventListener.capture( element, handlerBaseName, ReactEventEmitter.TopLevelCallbackCreator.createTopLevelCallback( diff --git a/src/browser/ui/dom/components/LocalEventTrapMixin.js b/src/browser/ui/dom/components/LocalEventTrapMixin.js new file mode 100644 index 0000000000000..1e4ec4f101d2f --- /dev/null +++ b/src/browser/ui/dom/components/LocalEventTrapMixin.js @@ -0,0 +1,52 @@ +/** + * Copyright 2014 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule LocalEventTrapMixin + */ + +"use strict"; + +var ReactEventEmitter = require('ReactEventEmitter'); + +var accumulate = require('accumulate'); +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'); + var listener = ReactEventEmitter.trapBubbledEvent( + topLevelType, + handlerBaseName, + this.getDOMNode() + ); + this._localEventListeners = accumulate(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; diff --git a/src/browser/ui/dom/components/ReactDOMForm.js b/src/browser/ui/dom/components/ReactDOMForm.js index b8696be86085b..867f046b03c38 100644 --- a/src/browser/ui/dom/components/ReactDOMForm.js +++ b/src/browser/ui/dom/components/ReactDOMForm.js @@ -18,11 +18,11 @@ "use strict"; +var EventConstants = require('EventConstants'); +var LocalEventTrapMixin = require('LocalEventTrapMixin'); var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin'); var ReactCompositeComponent = require('ReactCompositeComponent'); var ReactDOM = require('ReactDOM'); -var ReactEventEmitter = require('ReactEventEmitter'); -var EventConstants = require('EventConstants'); // Store a reference to the