diff --git a/packages/react-dom/src/client/ReactDOMComponent.js b/packages/react-dom/src/client/ReactDOMComponent.js index 5cc6981d82..d572d37a6b 100644 --- a/packages/react-dom/src/client/ReactDOMComponent.js +++ b/packages/react-dom/src/client/ReactDOMComponent.js @@ -59,7 +59,7 @@ import { import {getListenerMapForElement} from '../events/DOMEventListenerMap'; import { addResponderEventSystemEvent, - removeActiveResponderEventSystemEvent, + removeTrappedPassiveEventListener, } from '../events/ReactDOMEventListener.js'; import {mediaEventTypes} from '../events/DOMTopLevelEventTypes'; import { @@ -1360,7 +1360,7 @@ export function listenToEventResponderEventTypes( const passiveKey = targetEventType + '_passive'; const passiveListener = listenerMap.get(passiveKey); if (passiveListener != null) { - removeActiveResponderEventSystemEvent( + removeTrappedPassiveEventListener( document, targetEventType, passiveListener, diff --git a/packages/react-dom/src/events/DOMLegacyEventPluginSystem.js b/packages/react-dom/src/events/DOMLegacyEventPluginSystem.js index d7881ac690..8d0b6c4cf2 100644 --- a/packages/react-dom/src/events/DOMLegacyEventPluginSystem.js +++ b/packages/react-dom/src/events/DOMLegacyEventPluginSystem.js @@ -39,7 +39,7 @@ import { getRawEventName, mediaEventTypes, } from './DOMTopLevelEventTypes'; -import {trapEventForPluginEventSystem} from './ReactDOMEventListener'; +import {addTrappedEventListener} from './ReactDOMEventListener'; /** * Summary of `DOMEventPluginSystem` event handling: @@ -368,12 +368,12 @@ export function legacyTrapBubbledEvent( topLevelType: DOMTopLevelEventType, element: Document | Element, ): void { - trapEventForPluginEventSystem(element, topLevelType, false); + addTrappedEventListener(element, topLevelType, false); } export function legacyTrapCapturedEvent( topLevelType: DOMTopLevelEventType, element: Document | Element, ): void { - trapEventForPluginEventSystem(element, topLevelType, true); + addTrappedEventListener(element, topLevelType, true); } diff --git a/packages/react-dom/src/events/DOMModernPluginEventSystem.js b/packages/react-dom/src/events/DOMModernPluginEventSystem.js index daea82b498..ab2de7672c 100644 --- a/packages/react-dom/src/events/DOMModernPluginEventSystem.js +++ b/packages/react-dom/src/events/DOMModernPluginEventSystem.js @@ -21,7 +21,7 @@ import {plugins} from 'legacy-events/EventPluginRegistry'; import {HostRoot, HostPortal} from 'shared/ReactWorkTags'; -import {trapEventForPluginEventSystem} from './ReactDOMEventListener'; +import {addTrappedEventListener} from './ReactDOMEventListener'; import getEventTarget from './getEventTarget'; import {getListenerMapForElement} from './DOMEventListenerMap'; import { @@ -149,11 +149,7 @@ export function listenToTopLevelEvent( ): void { if (!listenerMap.has(topLevelType)) { const isCapturePhase = capturePhaseEvents.has(topLevelType); - trapEventForPluginEventSystem( - rootContainerElement, - topLevelType, - isCapturePhase, - ); + addTrappedEventListener(rootContainerElement, topLevelType, isCapturePhase); listenerMap.set(topLevelType, null); } } @@ -196,7 +192,7 @@ function willDeferLaterForFBLegacyPrimer(nativeEvent: any): boolean { if (node.tagName === 'A' && validFBLegacyPrimerRels.has(node.rel)) { const legacyFBSupport = true; const isCapture = nativeEvent.eventPhase === 1; - trapEventForPluginEventSystem( + addTrappedEventListener( document, ((type: any): DOMTopLevelEventType), isCapture, diff --git a/packages/react-dom/src/events/ReactDOMEventListener.js b/packages/react-dom/src/events/ReactDOMEventListener.js index 040029c46d..52b285b915 100644 --- a/packages/react-dom/src/events/ReactDOMEventListener.js +++ b/packages/react-dom/src/events/ReactDOMEventListener.js @@ -105,6 +105,7 @@ export function addResponderEventSystemEvent( } else { eventFlags |= IS_ACTIVE; } + let fbListener; // Check if interactive and wrap in discreteUpdates const listener = dispatchEvent.bind( null, @@ -113,39 +114,27 @@ export function addResponderEventSystemEvent( document, ); if (passiveBrowserEventsSupported) { - addEventCaptureListenerWithPassiveFlag( + fbListener = addEventCaptureListenerWithPassiveFlag( document, topLevelType, listener, passive, ); } else { - addEventCaptureListener(document, topLevelType, listener); + fbListener = addEventCaptureListener(document, topLevelType, listener); } - return listener; + // If we have an fbListener, then use that. + // We'll only have one if we use the forked + // EventListener-www module in FB builds. + return fbListener || listener; } -export function removeActiveResponderEventSystemEvent( - document: Document, - topLevelType: string, - listener: any => void, -) { - if (passiveBrowserEventsSupported) { - document.removeEventListener(topLevelType, listener, { - capture: true, - passive: false, - }); - } else { - document.removeEventListener(topLevelType, listener, true); - } -} - -export function trapEventForPluginEventSystem( +export function addTrappedEventListener( container: Document | Element, topLevelType: DOMTopLevelEventType, capture: boolean, legacyFBSupport?: boolean, -): void { +): any => void { let listener; let listenerWrapper; switch (getEventPriorityForPluginSystem(topLevelType)) { @@ -203,6 +192,29 @@ export function trapEventForPluginEventSystem( } else { fbListener = addEventBubbleListener(container, rawEventName, listener); } + // If we have an fbListener, then use that. + // We'll only have one if we use the forked + // EventListener-www module in FB builds. + return fbListener || listener; +} + +export function removeTrappedPassiveEventListener( + document: Document, + topLevelType: string, + listener: any => void, +) { + if (listener.remove != null) { + listener.remove(); + } else { + if (passiveBrowserEventsSupported) { + document.removeEventListener(topLevelType, listener, { + capture: true, + passive: true, + }); + } else { + document.removeEventListener(topLevelType, listener, true); + } + } } function dispatchDiscreteEvent( diff --git a/packages/react-dom/src/events/__tests__/DeprecatedDOMEventResponderSystem-test.internal.js b/packages/react-dom/src/events/__tests__/DeprecatedDOMEventResponderSystem-test.internal.js index c3fa398e83..62fda3edfb 100644 --- a/packages/react-dom/src/events/__tests__/DeprecatedDOMEventResponderSystem-test.internal.js +++ b/packages/react-dom/src/events/__tests__/DeprecatedDOMEventResponderSystem-test.internal.js @@ -948,4 +948,39 @@ describe('DOMEventResponderSystem', () => { document.body.removeChild(domNode); expect(onEvent).toBeCalled(); }); + + it('event upgrading should work correctly', () => { + let eventResponderFiredCount = 0; + const buttonRef = React.createRef(); + + const TestResponder = createEventResponder({ + targetEventTypes: ['click'], + onEvent: (event, context, props, state) => { + eventResponderFiredCount++; + if (!state.addedRootEventTypes) { + context.addRootEventTypes(['click_active']); + } + state.addedRootEventTypes = true; + }, + }); + + function Test() { + const listener = React.DEPRECATED_useResponder(TestResponder, {}); + + return ( + + ); + } + + ReactDOM.render(, container); + expect(container.innerHTML).toBe(''); + + let buttonElement = buttonRef.current; + dispatchClickEvent(buttonElement); + expect(eventResponderFiredCount).toBe(1); + dispatchClickEvent(buttonElement); + expect(eventResponderFiredCount).toBe(2); + }); });