Refactor ElementListenerMap (#18766)
Dominic Gannaway committed
Apr 28, 2020 at 21:53 UTC
88d0be6da5f0e3b57b010f852f90e603b38192da
8 files changed
+89
-92
packages/react-dom/src/client/ReactDOMComponent.js
+2
-2
@@ -59,7 +59,6 @@ import {
59
TOP_SUBMIT,
60
TOP_TOGGLE,
61
} from '../events/DOMTopLevelEventTypes';
62
-import {getListenerMapForElement} from '../events/DOMEventListenerMap';
62
import {mediaEventTypes} from '../events/DOMTopLevelEventTypes';
63
import {
64
createDangerousStringForStyles,
@@ -96,6 +95,7 @@ import {
95
legacyTrapBubbledEvent,
96
} from '../events/DOMLegacyEventPluginSystem';
97
import {listenToEvent} from '../events/DOMModernPluginEventSystem';
98
+import {getEventListenerMap} from './ReactDOMComponentTree';
99
100
let didWarnInvalidHydration = false;
101
let didWarnScriptTags = false;
@@ -1346,7 +1346,7 @@ export function listenToEventResponderEventTypes(
1346
if (enableDeprecatedFlareAPI) {
1347
// Get the listening Map for this element. We use this to track
1348
// what events we're listening to.
1349
- const listenerMap = getListenerMapForElement(document);
1349
+ const listenerMap = getEventListenerMap(document);
1350
1351
// Go through each target event type of the event responder
1352
for (let i = 0, length = eventTypes.length; i < length; ++i) {
packages/react-dom/src/client/ReactDOMComponentTree.js
+23
-3
@@ -15,6 +15,7 @@ import type {
15
SuspenseInstance,
16
Props,
17
} from './ReactDOMHostConfig';
18
+import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
19
20
import {
21
HostComponent,
@@ -30,8 +31,19 @@ const randomKey = Math.random()
31
.toString(36)
32
.slice(2);
33
const internalInstanceKey = '__reactFiber$' + randomKey;
33
-const internalEventHandlersKey = '__reactEvents$' + randomKey;
34
+const internalPropsKey = '__reactProps$' + randomKey;
35
const internalContainerInstanceKey = '__reactContainer$' + randomKey;
36
+const internalEventHandlersKey = '__reactEvents$' + randomKey;
37
+
38
+export type ElementListenerMap = Map<
39
+ DOMTopLevelEventType | string,
40
+ ElementListenerMapEntry,
41
+>;
42
+
43
+export type ElementListenerMapEntry = {
44
+ passive: void | boolean,
45
+ listener: any => void,
46
+};
47
48
export function precacheFiberNode(
49
hostInst: Fiber,
@@ -176,12 +188,20 @@ export function getNodeFromInstance(inst: Fiber): Instance | TextInstance {
188
export function getFiberCurrentPropsFromNode(
189
node: Instance | TextInstance | SuspenseInstance,
190
): Props {
179
- return (node: any)[internalEventHandlersKey] || null;
191
+ return (node: any)[internalPropsKey] || null;
192
}
193
194
export function updateFiberProps(
195
node: Instance | TextInstance | SuspenseInstance,
196
props: Props,
197
): void {
186
- (node: any)[internalEventHandlersKey] = props;
198
+ (node: any)[internalPropsKey] = props;
199
+}
200
+
201
+export function getEventListenerMap(node: EventTarget): ElementListenerMap {
202
+ let elementListenerMap = (node: any)[internalEventHandlersKey];
203
+ if (elementListenerMap === undefined) {
204
+ elementListenerMap = (node: any)[internalEventHandlersKey] = new Map();
205
+ }
206
+ return elementListenerMap;
207
}
packages/react-dom/src/events/DOMEventListenerMap.js
deleted
-70
@@ -1,70 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @flow
8
- */
9
-
10
-import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
11
-
12
-import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
13
-
14
-const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
15
-// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
16
-const elementListenerMap: WeakMap<
17
- EventTarget,
18
- ElementListenerMap,
19
-> = new PossiblyWeakMap();
20
-
21
-export type ElementListenerMap = Map<
22
- DOMTopLevelEventType | string,
23
- ElementListenerMapEntry,
24
->;
25
-
26
-export type ElementListenerMapEntry = {
27
- passive: void | boolean,
28
- listener: any => void,
29
-};
30
-
31
-export function getListenerMapForElement(
32
- target: EventTarget,
33
-): ElementListenerMap {
34
- let listenerMap = elementListenerMap.get(target);
35
- if (listenerMap === undefined) {
36
- listenerMap = new Map();
37
- elementListenerMap.set(target, listenerMap);
38
- }
39
- return listenerMap;
40
-}
41
-
42
-export function isListeningToAllDependencies(
43
- registrationName: string,
44
- mountAt: Document | Element,
45
-): boolean {
46
- const dependencies = registrationNameDependencies[registrationName];
47
- return isListeningToEvents(dependencies, mountAt);
48
-}
49
-
50
-export function isListeningToEvents(
51
- events: Array<string>,
52
- mountAt: Document | Element,
53
-): boolean {
54
- const listenerMap = getListenerMapForElement(mountAt);
55
- for (let i = 0; i < events.length; i++) {
56
- const event = events[i];
57
- if (!listenerMap.has(event)) {
58
- return false;
59
- }
60
- }
61
- return true;
62
-}
63
-
64
-export function isListeningToEvent(
65
- registrationName: string,
66
- mountAt: Document | Element,
67
-): boolean {
68
- const listenerMap = getListenerMapForElement(mountAt);
69
- return listenerMap.has(registrationName);
70
-}
packages/react-dom/src/events/DOMLegacyEventPluginSystem.js
+6
-4
@@ -9,7 +9,7 @@
9
10
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
11
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
12
-import type {ElementListenerMap} from '../events/DOMEventListenerMap';
12
+import type {ElementListenerMap} from '../client/ReactDOMComponentTree';
13
import type {EventSystemFlags} from './EventSystemFlags';
14
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
15
import type {PluginModule} from 'legacy-events/PluginModuleType';
@@ -29,8 +29,10 @@ import accumulateInto from 'legacy-events/accumulateInto';
29
import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
30
31
import getEventTarget from './getEventTarget';
32
-import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
33
-import {getListenerMapForElement} from './DOMEventListenerMap';
32
+import {
33
+ getClosestInstanceFromNode,
34
+ getEventListenerMap,
35
+} from '../client/ReactDOMComponentTree';
36
import isEventSupported from './isEventSupported';
37
import {
38
TOP_BLUR,
@@ -319,7 +321,7 @@ export function legacyListenToEvent(
321
registrationName: string,
322
mountAt: Document | Element,
323
): void {
322
- const listenerMap = getListenerMapForElement(mountAt);
324
+ const listenerMap = getEventListenerMap(mountAt);
325
const dependencies = registrationNameDependencies[registrationName];
326
327
for (let i = 0; i < dependencies.length; i++) {
packages/react-dom/src/events/DOMModernPluginEventSystem.js
+7
-5
@@ -12,7 +12,7 @@ import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
12
import type {
13
ElementListenerMap,
14
ElementListenerMapEntry,
15
-} from '../events/DOMEventListenerMap';
15
+} from '../client/ReactDOMComponentTree';
16
import type {EventSystemFlags} from './EventSystemFlags';
17
import type {EventPriority, ReactScopeMethods} from 'shared/ReactTypes';
18
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
@@ -36,7 +36,6 @@ import {
36
} from 'react-reconciler/src/ReactWorkTags';
37
38
import getEventTarget from './getEventTarget';
39
-import {getListenerMapForElement} from './DOMEventListenerMap';
39
import {
40
TOP_FOCUS,
41
TOP_LOAD,
@@ -74,7 +73,10 @@ import {
73
TOP_SELECTION_CHANGE,
74
getRawEventName,
75
} from './DOMTopLevelEventTypes';
77
-import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
76
+import {
77
+ getClosestInstanceFromNode,
78
+ getEventListenerMap,
79
+} from '../client/ReactDOMComponentTree';
80
import {COMMENT_NODE} from '../shared/HTMLNodeType';
81
import {batchedEventUpdates} from './ReactDOMUpdateBatching';
82
import getListener from './getListener';
@@ -237,7 +239,7 @@ export function listenToTopLevelEvent(
239
// triggered on the document directly.
240
if (topLevelType === TOP_SELECTION_CHANGE) {
241
targetContainer = (targetContainer: any).ownerDocument || targetContainer;
240
- listenerMap = getListenerMapForElement(targetContainer);
242
+ listenerMap = getEventListenerMap(targetContainer);
243
}
244
const listenerEntry: ElementListenerMapEntry | void = listenerMap.get(
245
topLevelType,
@@ -262,7 +264,7 @@ export function listenToEvent(
264
registrationName: string,
265
rootContainerElement: Element,
266
): void {
265
- const listenerMap = getListenerMapForElement(rootContainerElement);
267
+ const listenerMap = getEventListenerMap(rootContainerElement);
268
const dependencies = registrationNameDependencies[registrationName];
269
270
for (let i = 0; i < dependencies.length; i++) {
packages/react-dom/src/events/ReactDOMEventReplaying.js
+4
-4
@@ -10,7 +10,7 @@
10
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
11
import type {Container, SuspenseInstance} from '../client/ReactDOMHostConfig';
12
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
13
-import type {ElementListenerMap} from '../events/DOMEventListenerMap';
13
+import type {ElementListenerMap} from '../client/ReactDOMComponentTree';
14
import type {EventSystemFlags} from './EventSystemFlags';
15
import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
16
@@ -31,10 +31,10 @@ import {
31
getSuspenseInstanceFromFiber,
32
} from 'react-reconciler/src/ReactFiberTreeReflection';
33
import {attemptToDispatchEvent} from './ReactDOMEventListener';
34
-import {getListenerMapForElement} from './DOMEventListenerMap';
34
import {
35
getInstanceFromNode,
36
getClosestInstanceFromNode,
37
+ getEventListenerMap,
38
} from '../client/ReactDOMComponentTree';
39
import {unsafeCastDOMTopLevelTypeToString} from 'legacy-events/TopLevelEventTypes';
40
import {HostRoot, SuspenseComponent} from 'react-reconciler/src/ReactWorkTags';
@@ -256,10 +256,10 @@ export function eagerlyTrapReplayableEvents(
256
container: Container,
257
document: Document,
258
) {
259
- const listenerMapForDoc = getListenerMapForElement(document);
259
+ const listenerMapForDoc = getEventListenerMap(document);
260
let listenerMapForContainer;
261
if (enableModernEventSystem) {
262
- listenerMapForContainer = getListenerMapForElement(container);
262
+ listenerMapForContainer = getEventListenerMap(container);
263
}
264
// Discrete
265
discreteReplayableEvents.forEach(topLevelType => {
packages/react-dom/src/events/plugins/LegacySelectEventPlugin.js
+21
-2
@@ -22,12 +22,16 @@ import {
22
TOP_SELECTION_CHANGE,
23
} from '../DOMTopLevelEventTypes';
24
import getActiveElement from '../../client/getActiveElement';
25
-import {getNodeFromInstance} from '../../client/ReactDOMComponentTree';
25
+import {
26
+ getNodeFromInstance,
27
+ getEventListenerMap,
28
+} from '../../client/ReactDOMComponentTree';
29
import {hasSelectionCapabilities} from '../../client/ReactInputSelection';
30
import {DOCUMENT_NODE} from '../../shared/HTMLNodeType';
28
-import {isListeningToAllDependencies} from '../DOMEventListenerMap';
31
import {accumulateTwoPhaseDispatches} from '../DOMLegacyEventPluginSystem';
32
33
+import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
34
+
35
const skipSelectionChangeEvent =
36
canUseDOM && 'documentMode' in document && document.documentMode <= 11;
37
@@ -143,6 +147,21 @@ function constructSelectEvent(nativeEvent, nativeEventTarget) {
147
return null;
148
}
149
150
+function isListeningToAllDependencies(
151
+ registrationName: string,
152
+ mountAt: Document | Element,
153
+): boolean {
154
+ const dependencies = registrationNameDependencies[registrationName];
155
+ const listenerMap = getEventListenerMap(mountAt);
156
+ for (let i = 0; i < dependencies.length; i++) {
157
+ const event = dependencies[i];
158
+ if (!listenerMap.has(event)) {
159
+ return false;
160
+ }
161
+ }
162
+ return true;
163
+}
164
+
165
/**
166
* This plugin creates an `onSelect` event that normalizes select events
167
* across form elements.
packages/react-dom/src/events/plugins/ModernSelectEventPlugin.js
+26
-2
@@ -22,10 +22,12 @@ import {
22
TOP_SELECTION_CHANGE,
23
} from '../DOMTopLevelEventTypes';
24
import getActiveElement from '../../client/getActiveElement';
25
-import {getNodeFromInstance} from '../../client/ReactDOMComponentTree';
25
+import {
26
+ getNodeFromInstance,
27
+ getEventListenerMap,
28
+} from '../../client/ReactDOMComponentTree';
29
import {hasSelectionCapabilities} from '../../client/ReactInputSelection';
30
import {DOCUMENT_NODE} from '../../shared/HTMLNodeType';
28
-import {isListeningToEvent, isListeningToEvents} from '../DOMEventListenerMap';
31
import {accumulateTwoPhaseListeners} from '../DOMModernPluginEventSystem';
32
33
const skipSelectionChangeEvent =
@@ -144,6 +146,28 @@ function constructSelectEvent(nativeEvent, nativeEventTarget) {
146
return null;
147
}
148
149
+function isListeningToEvents(
150
+ events: Array<string>,
151
+ mountAt: Document | Element,
152
+): boolean {
153
+ const listenerMap = getEventListenerMap(mountAt);
154
+ for (let i = 0; i < events.length; i++) {
155
+ const event = events[i];
156
+ if (!listenerMap.has(event)) {
157
+ return false;
158
+ }
159
+ }
160
+ return true;
161
+}
162
+
163
+function isListeningToEvent(
164
+ registrationName: string,
165
+ mountAt: Document | Element,
166
+): boolean {
167
+ const listenerMap = getEventListenerMap(mountAt);
168
+ return listenerMap.has(registrationName);
169
+}
170
+
171
/**
172
* This plugin creates an `onSelect` event that normalizes select events
173
* across form elements.