Move accumulateEventTargetListeners to its own module/function (#18407)
Dominic Gannaway committed
Apr 1, 2020 at 14:19 UTC
d6d5f5aabef12386e45acfd13bbd0e8a1043bd4c
3 files changed
+159
-134
packages/react-dom/src/events/SimpleEventPlugin.js
+20
-1
@@ -37,6 +37,9 @@ import SyntheticUIEvent from './SyntheticUIEvent';
37
import SyntheticWheelEvent from './SyntheticWheelEvent';
38
import getEventCharCode from './getEventCharCode';
39
import accumulateTwoPhaseListeners from './accumulateTwoPhaseListeners';
40
+import accumulateEventTargetListeners from './accumulateEventTargetListeners';
41
+import {IS_TARGET_EVENT_ONLY} from 'legacy-events/EventSystemFlags';
42
+import {enableUseEventAPI} from 'shared/ReactFeatureFlags';
43
44
// Only used in DEV for exhaustiveness validation.
45
const knownHTMLTopLevelTypes: Array<DOMTopLevelEventType> = [
@@ -197,7 +200,23 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
200
nativeEvent,
201
nativeEventTarget,
202
);
200
- accumulateTwoPhaseListeners(event, true, eventSystemFlags, targetContainer);
203
+
204
+ // For TargetEvent only accumulation, we do not traverse through
205
+ // the React tree looking for managed React DOM elements that have
206
+ // events. Instead we only check the EventTarget Store Map to see
207
+ // if the container has listeners for the particular phase we're
208
+ // interested in. This is because we attach the native event listener
209
+ // only in the given phase.
210
+ if (
211
+ enableUseEventAPI &&
212
+ eventSystemFlags !== undefined &&
213
+ eventSystemFlags & IS_TARGET_EVENT_ONLY &&
214
+ targetContainer != null
215
+ ) {
216
+ accumulateEventTargetListeners(event, targetContainer);
217
+ } else {
218
+ accumulateTwoPhaseListeners(event, true);
219
+ }
220
return event;
221
},
222
};
packages/react-dom/src/events/accumulateEventTargetListeners.js
new
+55
@@ -0,0 +1,55 @@
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
+import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
12
+
13
+import {eventTargetEventListenerStore} from './DOMModernPluginEventSystem';
14
+
15
+export default function accumulateEventTargetListeners(
16
+ event: ReactSyntheticEvent,
17
+ targetContainer: EventTarget,
18
+): void {
19
+ const dispatchListeners = [];
20
+ const dispatchInstances = [];
21
+ const eventTypeMap = eventTargetEventListenerStore.get(targetContainer);
22
+ if (eventTypeMap !== undefined) {
23
+ const type = ((event.type: any): DOMTopLevelEventType);
24
+ const listeners = eventTypeMap.get(type);
25
+ if (listeners !== undefined) {
26
+ const isCapturePhase = (event: any).eventPhase === 1;
27
+
28
+ if (isCapturePhase) {
29
+ const captureListeners = Array.from(listeners.captured);
30
+
31
+ for (let i = captureListeners.length - 1; i >= 0; i--) {
32
+ const listener = captureListeners[i];
33
+ const {callback} = listener;
34
+ dispatchListeners.push(callback);
35
+ dispatchInstances.push(targetContainer);
36
+ }
37
+ } else {
38
+ const bubbleListeners = Array.from(listeners.bubbled);
39
+
40
+ for (let i = 0; i < bubbleListeners.length; i++) {
41
+ const listener = bubbleListeners[i];
42
+ const {callback} = listener;
43
+ dispatchListeners.push(callback);
44
+ dispatchInstances.push(targetContainer);
45
+ }
46
+ }
47
+ }
48
+ }
49
+ // To prevent allocation to the event unless we actually
50
+ // have listeners we check the length of one of the arrays.
51
+ if (dispatchListeners.length > 0) {
52
+ event._dispatchListeners = dispatchListeners;
53
+ event._dispatchInstances = dispatchInstances;
54
+ }
55
+}
packages/react-dom/src/events/accumulateTwoPhaseListeners.js
+84
-133
@@ -8,7 +8,6 @@
8
*/
9
10
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
11
-import type {EventSystemFlags} from 'legacy-events/EventSystemFlags';
11
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
12
13
import {
@@ -23,162 +22,114 @@ import {
22
23
import getListener from 'legacy-events/getListener';
24
import {getListenersFromTarget} from '../client/ReactDOMComponentTree';
26
-import {IS_TARGET_EVENT_ONLY} from 'legacy-events/EventSystemFlags';
27
-import {
28
- eventTargetEventListenerStore,
29
- reactScopeListenerStore,
30
-} from './DOMModernPluginEventSystem';
25
+import {reactScopeListenerStore} from './DOMModernPluginEventSystem';
26
27
export default function accumulateTwoPhaseListeners(
28
event: ReactSyntheticEvent,
29
accumulateUseEventListeners?: boolean,
35
- eventSystemFlags?: EventSystemFlags,
36
- targetContainer?: null | EventTarget,
30
): void {
31
const phasedRegistrationNames = event.dispatchConfig.phasedRegistrationNames;
32
const dispatchListeners = [];
33
const dispatchInstances = [];
34
42
- // For TargetEvent only accumulation, we do not traverse through
43
- // the React tree looking for managed React DOM elements that have
44
- // events. Instead we only check the EventTarget Store Map to see
45
- // if the container has listeners for the particular phase we're
46
- // interested in. This is because we attach the native event listener
47
- // only in the given phase.
48
- if (
49
- enableUseEventAPI &&
50
- accumulateUseEventListeners &&
51
- eventSystemFlags !== undefined &&
52
- eventSystemFlags & IS_TARGET_EVENT_ONLY &&
53
- targetContainer != null
54
- ) {
55
- const eventTypeMap = eventTargetEventListenerStore.get(targetContainer);
56
- if (eventTypeMap !== undefined) {
57
- const type = ((event.type: any): DOMTopLevelEventType);
58
- const listeners = eventTypeMap.get(type);
59
- if (listeners !== undefined) {
60
- const isCapturePhase = (event: any).eventPhase === 1;
61
-
62
- if (isCapturePhase) {
63
- const captureListeners = Array.from(listeners.captured);
64
-
65
- for (let i = captureListeners.length - 1; i >= 0; i--) {
66
- const listener = captureListeners[i];
67
- const {callback} = listener;
68
- dispatchListeners.push(callback);
69
- dispatchInstances.push(targetContainer);
70
- }
71
- } else {
72
- const bubbleListeners = Array.from(listeners.bubbled);
73
-
74
- for (let i = 0; i < bubbleListeners.length; i++) {
75
- const listener = bubbleListeners[i];
76
- const {callback} = listener;
77
- dispatchListeners.push(callback);
78
- dispatchInstances.push(targetContainer);
79
- }
80
- }
81
- }
82
- }
83
- } else {
84
- const {bubbled, captured} = phasedRegistrationNames;
85
- // If we are not handling EventTarget only phase, then we're doing the
86
- // usual two phase accumulation using the React fiber tree to pick up
87
- // all relevant useEvent and on* prop events.
88
- let node = event._targetInst;
89
- let lastHostComponent = null;
35
+ const {bubbled, captured} = phasedRegistrationNames;
36
+ // If we are not handling EventTarget only phase, then we're doing the
37
+ // usual two phase accumulation using the React fiber tree to pick up
38
+ // all relevant useEvent and on* prop events.
39
+ let node = event._targetInst;
40
+ let lastHostComponent = null;
41
91
- // Accumulate all instances and listeners via the target -> root path.
92
- while (node !== null) {
93
- const {stateNode: instance, tag} = node;
94
- // Handle listeners that are on HostComponents (i.e. <div>)
95
- if (instance !== null && tag === HostComponent) {
96
- lastHostComponent = instance;
97
- // For useEvent listenrs
98
- if (
99
- enableModernEventSystem &&
100
- enableUseEventAPI &&
101
- accumulateUseEventListeners
102
- ) {
103
- // useEvent event listeners
104
- const targetType = event.type;
105
- const listeners = getListenersFromTarget(instance);
42
+ // Accumulate all instances and listeners via the target -> root path.
43
+ while (node !== null) {
44
+ const {stateNode: instance, tag} = node;
45
+ // Handle listeners that are on HostComponents (i.e. <div>)
46
+ if (instance !== null && tag === HostComponent) {
47
+ lastHostComponent = instance;
48
+ // For useEvent listenrs
49
+ if (
50
+ enableModernEventSystem &&
51
+ enableUseEventAPI &&
52
+ accumulateUseEventListeners
53
+ ) {
54
+ // useEvent event listeners
55
+ const targetType = event.type;
56
+ const listeners = getListenersFromTarget(instance);
57
107
- if (listeners !== null) {
108
- const listenersArr = Array.from(listeners);
109
- for (let i = 0; i < listenersArr.length; i++) {
110
- const listener = listenersArr[i];
111
- const {
112
- callback,
113
- event: {capture, type},
114
- } = listener;
115
- if (type === targetType) {
116
- if (capture === true) {
117
- dispatchListeners.unshift(callback);
118
- dispatchInstances.unshift(node);
119
- } else {
120
- dispatchListeners.push(callback);
121
- dispatchInstances.push(node);
122
- }
58
+ if (listeners !== null) {
59
+ const listenersArr = Array.from(listeners);
60
+ for (let i = 0; i < listenersArr.length; i++) {
61
+ const listener = listenersArr[i];
62
+ const {
63
+ callback,
64
+ event: {capture, type},
65
+ } = listener;
66
+ if (type === targetType) {
67
+ if (capture === true) {
68
+ dispatchListeners.unshift(callback);
69
+ dispatchInstances.unshift(node);
70
+ } else {
71
+ dispatchListeners.push(callback);
72
+ dispatchInstances.push(node);
73
}
74
}
75
}
76
}
127
- // Standard React on* listeners, i.e. onClick prop
128
- if (captured !== null) {
129
- const captureListener = getListener(node, captured);
130
- if (captureListener != null) {
131
- // Capture listeners/instances should go at the start, so we
132
- // unshift them to the start of the array.
133
- dispatchListeners.unshift(captureListener);
134
- dispatchInstances.unshift(node);
135
- }
77
+ }
78
+ // Standard React on* listeners, i.e. onClick prop
79
+ if (captured !== null) {
80
+ const captureListener = getListener(node, captured);
81
+ if (captureListener != null) {
82
+ // Capture listeners/instances should go at the start, so we
83
+ // unshift them to the start of the array.
84
+ dispatchListeners.unshift(captureListener);
85
+ dispatchInstances.unshift(node);
86
}
137
- if (bubbled !== null) {
138
- const bubbleListener = getListener(node, bubbled);
139
- if (bubbleListener != null) {
140
- // Bubble listeners/instances should go at the end, so we
141
- // push them to the end of the array.
142
- dispatchListeners.push(bubbleListener);
143
- dispatchInstances.push(node);
144
- }
87
+ }
88
+ if (bubbled !== null) {
89
+ const bubbleListener = getListener(node, bubbled);
90
+ if (bubbleListener != null) {
91
+ // Bubble listeners/instances should go at the end, so we
92
+ // push them to the end of the array.
93
+ dispatchListeners.push(bubbleListener);
94
+ dispatchInstances.push(node);
95
}
96
}
147
- if (
148
- enableModernEventSystem &&
149
- enableUseEventAPI &&
150
- enableScopeAPI &&
151
- accumulateUseEventListeners &&
152
- tag === ScopeComponent &&
153
- lastHostComponent !== null
154
- ) {
155
- const reactScope = instance.methods;
156
- const eventTypeMap = reactScopeListenerStore.get(reactScope);
157
- if (eventTypeMap !== undefined) {
158
- const type = ((event.type: any): DOMTopLevelEventType);
159
- const listeners = eventTypeMap.get(type);
160
- if (listeners !== undefined) {
161
- const captureListeners = Array.from(listeners.captured);
162
- const bubbleListeners = Array.from(listeners.bubbled);
97
+ }
98
+ if (
99
+ enableModernEventSystem &&
100
+ enableUseEventAPI &&
101
+ enableScopeAPI &&
102
+ accumulateUseEventListeners &&
103
+ tag === ScopeComponent &&
104
+ lastHostComponent !== null
105
+ ) {
106
+ const reactScope = instance.methods;
107
+ const eventTypeMap = reactScopeListenerStore.get(reactScope);
108
+ if (eventTypeMap !== undefined) {
109
+ const type = ((event.type: any): DOMTopLevelEventType);
110
+ const listeners = eventTypeMap.get(type);
111
+ if (listeners !== undefined) {
112
+ const captureListeners = Array.from(listeners.captured);
113
+ const bubbleListeners = Array.from(listeners.bubbled);
114
164
- for (let i = 0; i < captureListeners.length; i++) {
165
- const listener = captureListeners[i];
166
- const {callback} = listener;
167
- dispatchListeners.unshift(callback);
168
- dispatchInstances.unshift(((lastHostComponent: any): Element));
169
- }
170
- for (let i = 0; i < bubbleListeners.length; i++) {
171
- const listener = bubbleListeners[i];
172
- const {callback} = listener;
173
- dispatchListeners.push(callback);
174
- dispatchInstances.push(((lastHostComponent: any): Element));
175
- }
115
+ for (let i = 0; i < captureListeners.length; i++) {
116
+ const listener = captureListeners[i];
117
+ const {callback} = listener;
118
+ dispatchListeners.unshift(callback);
119
+ dispatchInstances.unshift(((lastHostComponent: any): Element));
120
+ }
121
+ for (let i = 0; i < bubbleListeners.length; i++) {
122
+ const listener = bubbleListeners[i];
123
+ const {callback} = listener;
124
+ dispatchListeners.push(callback);
125
+ dispatchInstances.push(((lastHostComponent: any): Element));
126
}
127
}
128
}
179
- node = node.return;
129
}
130
+ node = node.return;
131
}
132
+
133
// To prevent allocation to the event unless we actually
134
// have listeners we check the length of one of the arrays.
135
if (dispatchListeners.length > 0) {