diff --git a/packages/react-dom/src/client/ReactDOMComponent.js b/packages/react-dom/src/client/ReactDOMComponent.js index d572d37a6bc8..899eb47f4019 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, - removeTrappedPassiveEventListener, + removeTrappedEventListener, } from '../events/ReactDOMEventListener.js'; import {mediaEventTypes} from '../events/DOMTopLevelEventTypes'; import { @@ -1360,10 +1360,11 @@ export function listenToEventResponderEventTypes( const passiveKey = targetEventType + '_passive'; const passiveListener = listenerMap.get(passiveKey); if (passiveListener != null) { - removeTrappedPassiveEventListener( + removeTrappedEventListener( document, targetEventType, passiveListener, + true, ); } } diff --git a/packages/react-dom/src/events/DOMEventListenerMap.js b/packages/react-dom/src/events/DOMEventListenerMap.js index 0cdd003aed69..d0f0bbd9a34f 100644 --- a/packages/react-dom/src/events/DOMEventListenerMap.js +++ b/packages/react-dom/src/events/DOMEventListenerMap.js @@ -18,9 +18,14 @@ const elementListenerMap: | WeakMap | Map void)>> = new PossiblyWeakMap(); +export type ElementListenerMap = Map< + DOMTopLevelEventType | string, + null | (any => void), +>; + export function getListenerMapForElement( target: EventTarget, -): Map void)> { +): ElementListenerMap { let listenerMap = elementListenerMap.get(target); if (listenerMap === undefined) { listenerMap = new Map(); diff --git a/packages/react-dom/src/events/DOMModernPluginEventSystem.js b/packages/react-dom/src/events/DOMModernPluginEventSystem.js index cf3929397f72..fc7706e48dad 100644 --- a/packages/react-dom/src/events/DOMModernPluginEventSystem.js +++ b/packages/react-dom/src/events/DOMModernPluginEventSystem.js @@ -9,7 +9,9 @@ import type {AnyNativeEvent} from 'legacy-events/PluginModuleType'; import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes'; +import type {ElementListenerMap} from '../events/DOMEventListenerMap'; import type {EventSystemFlags} from 'legacy-events/EventSystemFlags'; +import type {EventPriority} from 'shared/ReactTypes'; import type {Fiber} from 'react-reconciler/src/ReactFiber'; import type {PluginModule} from 'legacy-events/PluginModuleType'; import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType'; @@ -154,9 +156,13 @@ function dispatchEventsForPlugins( export function listenToTopLevelEvent( topLevelType: DOMTopLevelEventType, targetContainer: EventTarget, - listenerMap: Map void)>, + listenerMap: ElementListenerMap, passive?: boolean, + priority?: EventPriority, ): void { + // TODO: we need to know if the listenerMap previously was passive + // and to check if we need to upgrade to active. This will come in + // a useEvent follow up PR. if (!listenerMap.has(topLevelType)) { const isCapturePhase = capturePhaseEvents.has(topLevelType); const listener = addTrappedEventListener( @@ -165,6 +171,7 @@ export function listenToTopLevelEvent( isCapturePhase, false, passive, + priority, ); listenerMap.set(topLevelType, listener); } @@ -354,7 +361,7 @@ function getNearestRootOrPortalContainer(instance: Element): Element { export function attachElementListener(listener: ReactDOMListener): void { const {event, target} = listener; - const {passive, type} = event; + const {passive, priority, type} = event; let containerEventTarget = target; // If we the target is a managed React element, then we need to // find the nearest root/portal contained to attach the event listener @@ -376,6 +383,7 @@ export function attachElementListener(listener: ReactDOMListener): void { containerEventTarget, listenerMap, passive, + priority, ); // Get the internal listeners Set from the target instance. let listeners = getListenersFromTarget(target); diff --git a/packages/react-dom/src/events/ReactDOMEventListener.js b/packages/react-dom/src/events/ReactDOMEventListener.js index 4a2da49b23ff..d1ad26afbc88 100644 --- a/packages/react-dom/src/events/ReactDOMEventListener.js +++ b/packages/react-dom/src/events/ReactDOMEventListener.js @@ -8,6 +8,7 @@ */ import type {AnyNativeEvent} from 'legacy-events/PluginModuleType'; +import type {EventPriority} from 'shared/ReactTypes'; import type {FiberRoot} from 'react-reconciler/src/ReactFiberRoot'; import type {Container, SuspenseInstance} from '../client/ReactDOMHostConfig'; import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes'; @@ -137,10 +138,15 @@ export function addTrappedEventListener( capture: boolean, legacyFBSupport?: boolean, passive?: boolean, + priority?: EventPriority, ): any => void { + const eventPriority = + priority === undefined + ? getEventPriorityForPluginSystem(topLevelType) + : priority; let listener; let listenerWrapper; - switch (getEventPriorityForPluginSystem(topLevelType)) { + switch (eventPriority) { case DiscreteEvent: listenerWrapper = dispatchDiscreteEvent; break; @@ -247,10 +253,11 @@ export function addTrappedEventListener( return fbListener || listener; } -export function removeTrappedPassiveEventListener( +export function removeTrappedEventListener( targetContainer: EventTarget, topLevelType: string, listener: any => void, + passive: boolean, ) { if (listener.remove != null) { listener.remove(); @@ -258,7 +265,7 @@ export function removeTrappedPassiveEventListener( if (passiveBrowserEventsSupported) { targetContainer.removeEventListener(topLevelType, listener, { capture: true, - passive: true, + passive, }); } else { targetContainer.removeEventListener(topLevelType, listener, true); diff --git a/packages/react-dom/src/events/ReactDOMEventReplaying.js b/packages/react-dom/src/events/ReactDOMEventReplaying.js index c64f72226d11..5c9d1f2a0246 100644 --- a/packages/react-dom/src/events/ReactDOMEventReplaying.js +++ b/packages/react-dom/src/events/ReactDOMEventReplaying.js @@ -10,6 +10,7 @@ import type {AnyNativeEvent} from 'legacy-events/PluginModuleType'; import type {Container, SuspenseInstance} from '../client/ReactDOMHostConfig'; import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes'; +import type {ElementListenerMap} from '../events/DOMEventListenerMap'; import type {EventSystemFlags} from 'legacy-events/EventSystemFlags'; import type {FiberRoot} from 'react-reconciler/src/ReactFiberRoot'; @@ -216,7 +217,7 @@ export function isReplayableDiscreteEvent( function trapReplayableEventForContainer( topLevelType: DOMTopLevelEventType, container: Container, - listenerMap: Map void)>, + listenerMap: ElementListenerMap, ) { listenToTopLevelEvent(topLevelType, ((container: any): Element), listenerMap); } @@ -224,7 +225,7 @@ function trapReplayableEventForContainer( function trapReplayableEventForDocument( topLevelType: DOMTopLevelEventType, document: Document, - listenerMap: Map void)>, + listenerMap: ElementListenerMap, ) { if (!enableModernEventSystem) { legacyListenToTopLevelEvent(topLevelType, document, listenerMap);