Skip to content

Commit 4e3545f

Browse files
authored
ReactDOMEventListener: clean up module (facebook#18713)
1 parent b58dec9 commit 4e3545f

8 files changed

+193
-204
lines changed

packages/react-dom/src/client/ReactDOMComponent.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
import {registrationNameModules} from 'legacy-events/EventPluginRegistry';
1111
import {canUseDOM} from 'shared/ExecutionEnvironment';
1212
import invariant from 'shared/invariant';
13-
import {setListenToResponderEventTypes} from '../events/DeprecatedDOMEventResponderSystem';
13+
import {
14+
setListenToResponderEventTypes,
15+
addResponderEventSystemEvent,
16+
removeTrappedEventListener,
17+
} from '../events/DeprecatedDOMEventResponderSystem';
1418

1519
import {
1620
getValueForAttribute,
@@ -56,10 +60,6 @@ import {
5660
TOP_TOGGLE,
5761
} from '../events/DOMTopLevelEventTypes';
5862
import {getListenerMapForElement} from '../events/DOMEventListenerMap';
59-
import {
60-
addResponderEventSystemEvent,
61-
removeTrappedEventListener,
62-
} from '../events/ReactDOMEventListener.js';
6363
import {mediaEventTypes} from '../events/DOMTopLevelEventTypes';
6464
import {
6565
createDangerousStringForStyles,

packages/react-dom/src/events/DOMLegacyEventPluginSystem.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ import {
4444
getRawEventName,
4545
mediaEventTypes,
4646
} from './DOMTopLevelEventTypes';
47-
import {addTrappedEventListener} from './ReactDOMEventListener';
47+
import {createEventListenerWrapperWithPriority} from './ReactDOMEventListener';
4848
import {batchedEventUpdates} from './ReactDOMUpdateBatching';
4949
import getListener from './getListener';
50+
import {addEventCaptureListener, addEventBubbleListener} from './EventListener';
5051

5152
/**
5253
* Summary of `DOMEventPluginSystem` event handling:
@@ -399,6 +400,24 @@ export function legacyTrapCapturedEvent(
399400
listenerMap.set(topLevelType, {passive: undefined, listener});
400401
}
401402

403+
function addTrappedEventListener(
404+
targetContainer: EventTarget,
405+
topLevelType: DOMTopLevelEventType,
406+
eventSystemFlags: EventSystemFlags,
407+
capture: boolean,
408+
): any => void {
409+
const rawEventName = getRawEventName(topLevelType);
410+
const listener = createEventListenerWrapperWithPriority(
411+
targetContainer,
412+
topLevelType,
413+
eventSystemFlags,
414+
);
415+
const unsubscribeListener = capture
416+
? addEventCaptureListener(targetContainer, rawEventName, listener)
417+
: addEventBubbleListener(targetContainer, rawEventName, listener);
418+
return unsubscribeListener;
419+
}
420+
402421
function getParent(inst: Object | null): Object | null {
403422
if (!inst) {
404423
return null;

packages/react-dom/src/events/DOMModernPluginEventSystem.js

Lines changed: 86 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ import {
3535
HostComponent,
3636
} from 'react-reconciler/src/ReactWorkTags';
3737

38-
import {
39-
addTrappedEventListener,
40-
removeTrappedEventListener,
41-
} from './ReactDOMEventListener';
4238
import getEventTarget from './getEventTarget';
4339
import {getListenerMapForElement} from './DOMEventListenerMap';
4440
import {
@@ -76,17 +72,25 @@ import {
7672
TOP_PLAYING,
7773
TOP_CLICK,
7874
TOP_SELECTION_CHANGE,
75+
getRawEventName,
7976
} from './DOMTopLevelEventTypes';
8077
import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
8178
import {COMMENT_NODE} from '../shared/HTMLNodeType';
8279
import {batchedEventUpdates} from './ReactDOMUpdateBatching';
8380
import getListener from './getListener';
81+
import {passiveBrowserEventsSupported} from './checkPassiveEvents';
8482

8583
import {enableLegacyFBSupport} from 'shared/ReactFeatureFlags';
8684
import {
8785
invokeGuardedCallbackAndCatchFirstError,
8886
rethrowCaughtError,
8987
} from 'shared/ReactErrorUtils';
88+
import {createEventListenerWrapperWithPriority} from './ReactDOMEventListener';
89+
import {
90+
removeEventListener,
91+
addEventCaptureListener,
92+
addEventBubbleListener,
93+
} from './EventListener';
9094

9195
const capturePhaseEvents = new Set([
9296
TOP_FOCUS,
@@ -219,25 +223,6 @@ function dispatchEventsForPlugins(
219223
dispatchEventsInBatch(syntheticEvents);
220224
}
221225

222-
function shouldUpgradeListener(
223-
listenerEntry: void | ElementListenerMapEntry,
224-
passive: void | boolean,
225-
): boolean {
226-
if (listenerEntry === undefined) {
227-
return false;
228-
}
229-
// Upgrade from passive to active.
230-
if (passive !== true && listenerEntry.passive) {
231-
return true;
232-
}
233-
// Upgrade from default-active (browser default) to active.
234-
if (passive === false && listenerEntry.passive === undefined) {
235-
return true;
236-
}
237-
// Otherwise, do not upgrade
238-
return false;
239-
}
240-
241226
export function listenToTopLevelEvent(
242227
topLevelType: DOMTopLevelEventType,
243228
targetContainer: EventTarget,
@@ -247,43 +232,19 @@ export function listenToTopLevelEvent(
247232
priority?: EventPriority,
248233
capture?: boolean,
249234
): void {
250-
// If we explicitly define capture, then these are for EventTarget objects,
251-
// rather than React managed DOM elements. So we need to ensure we separate
252-
// capture and non-capture events. For React managed DOM nodes we only use
253-
// one or the other, never both. Which one we use is determined by the the
254-
// capturePhaseEvents Set (in this module) that defines if the event listener
255-
// should use the capture phase – otherwise we always use the bubble phase.
256-
// Finally, when we get to dispatching and accumulating event listeners, we
257-
// check if the user wanted capture/bubble and emulate the behavior at that
258-
// point (we call this accumulating two phase listeners).
259-
const typeStr = ((topLevelType: any): string);
260-
const listenerMapKey =
261-
capture === undefined
262-
? topLevelType
263-
: `${typeStr}_${capture ? 'capture' : 'bubble'}`;
264-
265235
// TOP_SELECTION_CHANGE needs to be attached to the document
266236
// otherwise it won't capture incoming events that are only
267237
// triggered on the document directly.
268238
if (topLevelType === TOP_SELECTION_CHANGE) {
269239
targetContainer = (targetContainer: any).ownerDocument || targetContainer;
270240
listenerMap = getListenerMapForElement(targetContainer);
271241
}
272-
const listenerEntry = listenerMap.get(listenerMapKey);
273-
const shouldUpgrade = shouldUpgradeListener(listenerEntry, passive);
274-
if (listenerEntry === undefined || shouldUpgrade) {
275-
const isCapturePhase =
276-
capture === undefined ? capturePhaseEvents.has(topLevelType) : capture;
277-
// If we should upgrade, then we need to remove the existing trapped
278-
// event listener for the target container.
279-
if (shouldUpgrade) {
280-
removeTrappedEventListener(
281-
targetContainer,
282-
topLevelType,
283-
isCapturePhase,
284-
((listenerEntry: any): ElementListenerMapEntry).listener,
285-
);
286-
}
242+
const listenerEntry: ElementListenerMapEntry | void = listenerMap.get(
243+
topLevelType,
244+
);
245+
const isCapturePhase =
246+
capture === undefined ? capturePhaseEvents.has(topLevelType) : capture;
247+
if (listenerEntry === undefined) {
287248
const listener = addTrappedEventListener(
288249
targetContainer,
289250
topLevelType,
@@ -293,7 +254,7 @@ export function listenToTopLevelEvent(
293254
passive,
294255
priority,
295256
);
296-
listenerMap.set(listenerMapKey, {passive, listener});
257+
listenerMap.set(topLevelType, {passive, listener});
297258
}
298259
}
299260

@@ -315,6 +276,77 @@ export function listenToEvent(
315276
}
316277
}
317278

279+
function addTrappedEventListener(
280+
targetContainer: EventTarget,
281+
topLevelType: DOMTopLevelEventType,
282+
eventSystemFlags: EventSystemFlags,
283+
capture: boolean,
284+
isDeferredListenerForLegacyFBSupport?: boolean,
285+
passive?: boolean,
286+
priority?: EventPriority,
287+
): any => void {
288+
let listener = createEventListenerWrapperWithPriority(
289+
targetContainer,
290+
topLevelType,
291+
eventSystemFlags,
292+
priority,
293+
);
294+
// If passive option is not supported, then the event will be
295+
// active and not passive.
296+
if (passive === true && !passiveBrowserEventsSupported) {
297+
passive = false;
298+
}
299+
300+
targetContainer =
301+
enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport
302+
? (targetContainer: any).ownerDocument
303+
: targetContainer;
304+
305+
const rawEventName = getRawEventName(topLevelType);
306+
307+
let unsubscribeListener;
308+
// When legacyFBSupport is enabled, it's for when we
309+
// want to add a one time event listener to a container.
310+
// This should only be used with enableLegacyFBSupport
311+
// due to requirement to provide compatibility with
312+
// internal FB www event tooling. This works by removing
313+
// the event listener as soon as it is invoked. We could
314+
// also attempt to use the {once: true} param on
315+
// addEventListener, but that requires support and some
316+
// browsers do not support this today, and given this is
317+
// to support legacy code patterns, it's likely they'll
318+
// need support for such browsers.
319+
if (enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport) {
320+
const originalListener = listener;
321+
listener = function(...p) {
322+
try {
323+
return originalListener.apply(this, p);
324+
} finally {
325+
removeEventListener(
326+
targetContainer,
327+
rawEventName,
328+
unsubscribeListener,
329+
capture,
330+
);
331+
}
332+
};
333+
}
334+
if (capture) {
335+
unsubscribeListener = addEventCaptureListener(
336+
targetContainer,
337+
rawEventName,
338+
listener,
339+
);
340+
} else {
341+
unsubscribeListener = addEventBubbleListener(
342+
targetContainer,
343+
rawEventName,
344+
listener,
345+
);
346+
}
347+
return unsubscribeListener;
348+
}
349+
318350
function willDeferLaterForLegacyFBSupport(
319351
topLevelType: DOMTopLevelEventType,
320352
targetContainer: EventTarget,

packages/react-dom/src/events/DeprecatedDOMEventResponderSystem.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
type EventSystemFlags,
1111
IS_PASSIVE,
1212
PASSIVE_NOT_SUPPORTED,
13+
RESPONDER_EVENT_SYSTEM,
1314
} from './EventSystemFlags';
1415
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
1516
import {
@@ -37,6 +38,15 @@ import invariant from 'shared/invariant';
3738

3839
import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
3940
import {enqueueStateRestore} from './ReactDOMControlledComponent';
41+
import {createEventListenerWrapper} from './ReactDOMEventListener';
42+
import {passiveBrowserEventsSupported} from './checkPassiveEvents';
43+
import {getRawEventName} from './DOMTopLevelEventTypes';
44+
import {
45+
addEventCaptureListener,
46+
addEventCaptureListenerWithPassiveFlag,
47+
removeEventListener,
48+
} from './EventListener';
49+
4050
import {
4151
ContinuousEvent,
4252
UserBlockingEvent,
@@ -571,3 +581,50 @@ function DEPRECATED_registerRootEventType(
571581
rootEventTypesSet.add(rootEventType);
572582
rootEventResponderInstances.add(eventResponderInstance);
573583
}
584+
585+
export function addResponderEventSystemEvent(
586+
document: Document,
587+
topLevelType: string,
588+
passive: boolean,
589+
): any => void {
590+
let eventFlags = RESPONDER_EVENT_SYSTEM;
591+
592+
// If passive option is not supported, then the event will be
593+
// active and not passive, but we flag it as using not being
594+
// supported too. This way the responder event plugins know,
595+
// and can provide polyfills if needed.
596+
if (passive) {
597+
if (passiveBrowserEventsSupported) {
598+
eventFlags |= IS_PASSIVE;
599+
} else {
600+
eventFlags |= PASSIVE_NOT_SUPPORTED;
601+
passive = false;
602+
}
603+
}
604+
// Check if interactive and wrap in discreteUpdates
605+
const listener = createEventListenerWrapper(
606+
document,
607+
((topLevelType: any): DOMTopLevelEventType),
608+
eventFlags,
609+
);
610+
if (passiveBrowserEventsSupported) {
611+
return addEventCaptureListenerWithPassiveFlag(
612+
document,
613+
topLevelType,
614+
listener,
615+
passive,
616+
);
617+
} else {
618+
return addEventCaptureListener(document, topLevelType, listener);
619+
}
620+
}
621+
622+
export function removeTrappedEventListener(
623+
targetContainer: EventTarget,
624+
topLevelType: DOMTopLevelEventType,
625+
capture: boolean,
626+
listener: any => void,
627+
): void {
628+
const rawEventName = getRawEventName(topLevelType);
629+
removeEventListener(targetContainer, rawEventName, listener, capture);
630+
}

0 commit comments

Comments
 (0)