@samitouri / QOS-React-2 / commits / 88d0be6da5

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.