Modern Event System: improve dispatching queue (#18799)
Dominic Gannaway committed
May 4, 2020 at 16:55 UTC
9751935abfa5585623f3ee85bb3c4eff4154e376
14 files changed
+258
-179
packages/legacy-events/EventPluginRegistry.js
+15
-5
@@ -11,12 +11,18 @@ import type {DispatchConfig} from './ReactSyntheticEventType';
11
import type {
12
AnyNativeEvent,
13
PluginName,
14
- PluginModule,
14
+ LegacyPluginModule,
15
+ ModernPluginModule,
16
} from './PluginModuleType';
17
18
import invariant from 'shared/invariant';
19
19
-type NamesToPlugins = {[key: PluginName]: PluginModule<AnyNativeEvent>, ...};
20
+type NamesToPlugins = {
21
+ [key: PluginName]:
22
+ | LegacyPluginModule<AnyNativeEvent>
23
+ | ModernPluginModule<AnyNativeEvent>,
24
+ ...,
25
+};
26
type EventPluginOrder = null | Array<PluginName>;
27
28
/**
@@ -84,7 +90,9 @@ function recomputePluginOrdering(): void {
90
*/
91
function publishEventForPlugin(
92
dispatchConfig: DispatchConfig,
87
- pluginModule: PluginModule<AnyNativeEvent>,
93
+ pluginModule:
94
+ | LegacyPluginModule<AnyNativeEvent>
95
+ | ModernPluginModule<AnyNativeEvent>,
96
eventName: string,
97
): boolean {
98
invariant(
@@ -128,7 +136,9 @@ function publishEventForPlugin(
136
*/
137
function publishRegistrationName(
138
registrationName: string,
131
- pluginModule: PluginModule<AnyNativeEvent>,
139
+ pluginModule:
140
+ | LegacyPluginModule<AnyNativeEvent>
141
+ | ModernPluginModule<AnyNativeEvent>,
142
eventName: string,
143
): void {
144
invariant(
@@ -243,7 +253,7 @@ export function injectEventPluginsByName(
253
}
254
255
export function injectEventPlugins(
246
- eventPlugins: [PluginModule<AnyNativeEvent>],
256
+ eventPlugins: [ModernPluginModule<AnyNativeEvent>],
257
): void {
258
for (let i = 0; i < eventPlugins.length; i++) {
259
const pluginModule = eventPlugins[i];
packages/legacy-events/PluginModuleType.js
+30
-1
@@ -22,7 +22,7 @@ export type PluginName = string;
22
23
export type EventSystemFlags = number;
24
25
-export type PluginModule<NativeEvent> = {
25
+export type LegacyPluginModule<NativeEvent> = {
26
eventTypes: EventTypes,
27
extractEvents: (
28
topLevelType: TopLevelType,
@@ -34,3 +34,32 @@ export type PluginModule<NativeEvent> = {
34
) => ?ReactSyntheticEvent,
35
tapMoveThreshold?: number,
36
};
37
+
38
+export type DispatchQueueItemPhaseEntry = {|
39
+ instance: null | Fiber,
40
+ listener: Function,
41
+ currentTarget: EventTarget,
42
+|};
43
+
44
+export type DispatchQueueItemPhase = Array<DispatchQueueItemPhaseEntry>;
45
+
46
+export type DispatchQueueItem = {|
47
+ event: ReactSyntheticEvent,
48
+ capture: DispatchQueueItemPhase,
49
+ bubble: DispatchQueueItemPhase,
50
+|};
51
+
52
+export type DispatchQueue = Array<DispatchQueueItem>;
53
+
54
+export type ModernPluginModule<NativeEvent> = {
55
+ eventTypes: EventTypes,
56
+ extractEvents: (
57
+ dispatchQueue: DispatchQueue,
58
+ topLevelType: TopLevelType,
59
+ targetInst: null | Fiber,
60
+ nativeTarget: NativeEvent,
61
+ nativeEventTarget: null | EventTarget,
62
+ eventSystemFlags: number,
63
+ container: null | EventTarget,
64
+ ) => void,
65
+};
packages/legacy-events/ReactSyntheticEventType.js
+2
-3
@@ -41,9 +41,8 @@ export type ReactSyntheticEvent = {|
41
) => ReactSyntheticEvent,
42
isPersistent: () => boolean,
43
isPropagationStopped: () => boolean,
44
- _dispatchInstances: null | Array<Fiber | null> | Fiber,
45
- _dispatchListeners: null | Array<Function> | Function,
46
- _dispatchCurrentTargets: null | Array<EventTarget>,
44
+ _dispatchInstances?: null | Array<Fiber | null> | Fiber,
45
+ _dispatchListeners?: null | Array<Function> | Function,
46
_targetInst: Fiber,
47
type: string,
48
currentTarget: null | EventTarget,
packages/legacy-events/SyntheticEvent.js
+9
-6
@@ -8,6 +8,7 @@
8
/* eslint valid-typeof: 0 */
9
10
import invariant from 'shared/invariant';
11
+import {enableModernEventSystem} from 'shared/ReactFeatureFlags';
12
13
const EVENT_POOL_SIZE = 10;
14
@@ -76,9 +77,10 @@ function SyntheticEvent(
77
this.dispatchConfig = dispatchConfig;
78
this._targetInst = targetInst;
79
this.nativeEvent = nativeEvent;
79
- this._dispatchListeners = null;
80
- this._dispatchInstances = null;
81
- this._dispatchCurrentTargets = null;
80
+ if (!enableModernEventSystem) {
81
+ this._dispatchListeners = null;
82
+ this._dispatchInstances = null;
83
+ }
84
85
const Interface = this.constructor.Interface;
86
for (const propName in Interface) {
@@ -186,9 +188,10 @@ Object.assign(SyntheticEvent.prototype, {
188
this.nativeEvent = null;
189
this.isDefaultPrevented = functionThatReturnsFalse;
190
this.isPropagationStopped = functionThatReturnsFalse;
189
- this._dispatchListeners = null;
190
- this._dispatchInstances = null;
191
- this._dispatchCurrentTargets = null;
191
+ if (!enableModernEventSystem) {
192
+ this._dispatchListeners = null;
193
+ this._dispatchInstances = null;
194
+ }
195
if (__DEV__) {
196
Object.defineProperty(
197
this,
packages/react-dom/src/events/DOMLegacyEventPluginSystem.js
+4
-3
@@ -12,7 +12,7 @@ import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
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';
15
+import type {LegacyPluginModule} from 'legacy-events/PluginModuleType';
16
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
17
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
18
import forEachAccumulated from 'legacy-events/forEachAccumulated';
@@ -191,9 +191,10 @@ function extractPluginEvents(
191
eventSystemFlags: EventSystemFlags,
192
): Array<ReactSyntheticEvent> | ReactSyntheticEvent | null {
193
let events = null;
194
- for (let i = 0; i < plugins.length; i++) {
194
+ const legacyPlugins = ((plugins: any): Array<LegacyPluginModule<Event>>);
195
+ for (let i = 0; i < legacyPlugins.length; i++) {
196
// Not every plugin in the ordering may be loaded at runtime.
196
- const possiblePlugin: PluginModule<AnyNativeEvent> = plugins[i];
197
+ const possiblePlugin = legacyPlugins[i];
198
if (possiblePlugin) {
199
const extractedEvents = possiblePlugin.extractEvents(
200
topLevelType,
packages/react-dom/src/events/DOMModernPluginEventSystem.js
+129
-97
@@ -16,7 +16,13 @@ import type {
16
import type {EventSystemFlags} from './EventSystemFlags';
17
import type {EventPriority} from 'shared/ReactTypes';
18
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
19
-import type {PluginModule} from 'legacy-events/PluginModuleType';
19
+import type {
20
+ ModernPluginModule,
21
+ DispatchQueue,
22
+ DispatchQueueItem,
23
+ DispatchQueueItemPhase,
24
+ DispatchQueueItemPhaseEntry,
25
+} from 'legacy-events/PluginModuleType';
26
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
27
28
import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
@@ -130,8 +136,6 @@ const capturePhaseEvents = new Set([
136
TOP_WAITING,
137
]);
138
133
-const isArray = Array.isArray;
134
-
139
function executeDispatch(
140
event: ReactSyntheticEvent,
141
listener: Function,
@@ -143,50 +147,41 @@ function executeDispatch(
147
event.currentTarget = null;
148
}
149
146
-function executeDispatchesInOrder(event: ReactSyntheticEvent): void {
147
- // TODO we should remove _dispatchListeners and _dispatchInstances at some point.
148
- const dispatchListeners = event._dispatchListeners;
149
- const dispatchInstances = event._dispatchInstances;
150
- const dispatchCurrentTargets = event._dispatchCurrentTargets;
150
+function executeDispatchesInOrder(
151
+ event: ReactSyntheticEvent,
152
+ capture: DispatchQueueItemPhase,
153
+ bubble: DispatchQueueItemPhase,
154
+): void {
155
let previousInstance;
152
-
153
- if (
154
- dispatchListeners !== null &&
155
- dispatchInstances !== null &&
156
- dispatchCurrentTargets !== null
157
- ) {
158
- for (let i = 0; i < dispatchListeners.length; i++) {
159
- const instance = dispatchInstances[i];
160
- const listener = dispatchListeners[i];
161
- const currentTarget = dispatchCurrentTargets[i];
162
-
163
- // We check if the instance was the same as the last one,
164
- // if it was, then we're still on the same instance thus
165
- // propagation should not stop. If we add support for
166
- // stopImmediatePropagation at some point, then we'll
167
- // need to handle that case here differently.
168
- if (instance !== previousInstance && event.isPropagationStopped()) {
169
- break;
170
- }
171
- // Listeners and Instances are two parallel arrays that are always in sync.
172
- executeDispatch(event, listener, currentTarget);
173
- previousInstance = instance;
156
+ // Dispatch capture phase first.
157
+ for (let i = capture.length - 1; i >= 0; i--) {
158
+ const {instance, currentTarget, listener} = capture[i];
159
+ if (instance !== previousInstance && event.isPropagationStopped()) {
160
+ return;
161
}
162
+ executeDispatch(event, listener, currentTarget);
163
+ previousInstance = instance;
164
+ }
165
+ previousInstance = undefined;
166
+ // Dispatch bubble phase second.
167
+ for (let i = 0; i < bubble.length; i++) {
168
+ const {instance, currentTarget, listener} = bubble[i];
169
+ if (instance !== previousInstance && event.isPropagationStopped()) {
170
+ return;
171
+ }
172
+ executeDispatch(event, listener, currentTarget);
173
+ previousInstance = instance;
174
}
176
- event._dispatchListeners = null;
177
- event._dispatchInstances = null;
178
- event._dispatchCurrentTargets = null;
175
}
176
181
-export function dispatchEventsInBatch(
182
- events: Array<ReactSyntheticEvent>,
183
-): void {
184
- for (let i = 0; i < events.length; i++) {
185
- const syntheticEvent = events[i];
186
- executeDispatchesInOrder(syntheticEvent);
177
+export function dispatchEventsInBatch(dispatchQueue: DispatchQueue): void {
178
+ for (let i = 0; i < dispatchQueue.length; i++) {
179
+ const dispatchQueueItem: DispatchQueueItem = dispatchQueue[i];
180
+ const {event, capture, bubble} = dispatchQueueItem;
181
+ executeDispatchesInOrder(event, capture, bubble);
182
// Release the event from the pool if needed
188
- if (!syntheticEvent.isPersistent()) {
189
- syntheticEvent.constructor.release(syntheticEvent);
183
+ if (!event.isPersistent()) {
184
+ event.constructor.release(event);
185
}
186
}
187
// This would be a good time to rethrow if any of the event handlers threw.
@@ -200,12 +195,14 @@ function dispatchEventsForPlugins(
195
targetInst: null | Fiber,
196
targetContainer: EventTarget,
197
): void {
198
+ const modernPlugins = ((plugins: any): Array<ModernPluginModule<Event>>);
199
const nativeEventTarget = getEventTarget(nativeEvent);
204
- const syntheticEvents: Array<ReactSyntheticEvent> = [];
200
+ const dispatchQueue: DispatchQueue = [];
201
206
- for (let i = 0; i < plugins.length; i++) {
207
- const possiblePlugin: PluginModule<AnyNativeEvent> = plugins[i];
208
- const extractedEvents = possiblePlugin.extractEvents(
202
+ for (let i = 0; i < modernPlugins.length; i++) {
203
+ const plugin = modernPlugins[i];
204
+ plugin.extractEvents(
205
+ dispatchQueue,
206
topLevelType,
207
targetInst,
208
nativeEvent,
@@ -213,16 +210,8 @@ function dispatchEventsForPlugins(
210
eventSystemFlags,
211
targetContainer,
212
);
216
- if (isArray(extractedEvents)) {
217
- // Flow complains about @@iterator being missing in ReactSyntheticEvent,
218
- // so we cast to avoid the Flow error.
219
- const arrOfExtractedEvents = ((extractedEvents: any): Array<ReactSyntheticEvent>);
220
- syntheticEvents.push(...arrOfExtractedEvents);
221
- } else if (extractedEvents != null) {
222
- syntheticEvents.push(extractedEvents);
223
- }
213
}
225
- dispatchEventsInBatch(syntheticEvents);
214
+ dispatchEventsInBatch(dispatchQueue);
215
}
216
217
export function listenToTopLevelEvent(
@@ -480,17 +469,44 @@ export function dispatchEventForPluginEventSystem(
469
);
470
}
471
483
-export function accumulateTwoPhaseListeners(event: ReactSyntheticEvent): void {
472
+function createDispatchQueueItemPhaseEntry(
473
+ instance: null | Fiber,
474
+ listener: Function,
475
+ currentTarget: EventTarget,
476
+): DispatchQueueItemPhaseEntry {
477
+ return {
478
+ instance,
479
+ listener,
480
+ currentTarget,
481
+ };
482
+}
483
+
484
+function createDispatchQueueItem(
485
+ event: ReactSyntheticEvent,
486
+ capture: DispatchQueueItemPhase,
487
+ bubble: DispatchQueueItemPhase,
488
+): DispatchQueueItem {
489
+ return {
490
+ event,
491
+ capture,
492
+ bubble,
493
+ };
494
+}
495
+
496
+export function accumulateTwoPhaseListeners(
497
+ targetFiber: Fiber | null,
498
+ dispatchQueue: DispatchQueue,
499
+ event: ReactSyntheticEvent,
500
+): void {
501
const phasedRegistrationNames = event.dispatchConfig.phasedRegistrationNames;
485
- const dispatchListeners = [];
486
- const dispatchInstances: Array<Fiber | null> = [];
487
- const dispatchCurrentTargets = [];
502
+ const capturePhase: DispatchQueueItemPhase = [];
503
+ const bubblePhase: DispatchQueueItemPhase = [];
504
505
const {bubbled, captured} = phasedRegistrationNames;
506
// If we are not handling EventTarget only phase, then we're doing the
507
// usual two phase accumulation using the React fiber tree to pick up
508
// all relevant useEvent and on* prop events.
493
- let instance = event._targetInst;
509
+ let instance = targetFiber;
510
511
// Accumulate all instances and listeners via the target -> root path.
512
while (instance !== null) {
@@ -502,33 +518,34 @@ export function accumulateTwoPhaseListeners(event: ReactSyntheticEvent): void {
518
if (captured !== null) {
519
const captureListener = getListener(instance, captured);
520
if (captureListener != null) {
505
- // Capture listeners/instances should go at the start, so we
506
- // unshift them to the start of the array.
507
- dispatchListeners.unshift(captureListener);
508
- dispatchInstances.unshift(instance);
509
- dispatchCurrentTargets.unshift(currentTarget);
521
+ capturePhase.push(
522
+ createDispatchQueueItemPhaseEntry(
523
+ instance,
524
+ captureListener,
525
+ currentTarget,
526
+ ),
527
+ );
528
}
529
}
530
if (bubbled !== null) {
531
const bubbleListener = getListener(instance, bubbled);
532
if (bubbleListener != null) {
515
- // Bubble listeners/instances should go at the end, so we
516
- // push them to the end of the array.
517
- dispatchListeners.push(bubbleListener);
518
- dispatchInstances.push(instance);
519
- dispatchCurrentTargets.push(currentTarget);
533
+ bubblePhase.push(
534
+ createDispatchQueueItemPhaseEntry(
535
+ instance,
536
+ bubbleListener,
537
+ currentTarget,
538
+ ),
539
+ );
540
}
541
}
542
}
543
instance = instance.return;
544
}
525
-
526
- // To prevent allocation to the event unless we actually
527
- // have listeners we check the length of one of the arrays.
528
- if (dispatchListeners.length > 0) {
529
- event._dispatchListeners = dispatchListeners;
530
- event._dispatchInstances = dispatchInstances;
531
- event._dispatchCurrentTargets = dispatchCurrentTargets;
545
+ if (capturePhase.length !== 0 || bubblePhase.length !== 0) {
546
+ dispatchQueue.push(
547
+ createDispatchQueueItem(event, capturePhase, bubblePhase),
548
+ );
549
}
550
}
551
@@ -591,6 +608,7 @@ function getLowestCommonAncestor(instA: Fiber, instB: Fiber): Fiber | null {
608
}
609
610
function accumulateEnterLeaveListenersForEvent(
611
+ dispatchQueue: DispatchQueue,
612
event: ReactSyntheticEvent,
613
target: Fiber,
614
common: Fiber | null,
@@ -600,9 +618,8 @@ function accumulateEnterLeaveListenersForEvent(
618
if (registrationName === undefined) {
619
return;
620
}
603
- const dispatchListeners = [];
604
- const dispatchInstances: Array<Fiber | null> = [];
605
- const dispatchCurrentTargets = [];
621
+ const capturePhase: DispatchQueueItemPhase = [];
622
+ const bubblePhase: DispatchQueueItemPhase = [];
623
624
let instance = target;
625
while (instance !== null) {
@@ -618,35 +635,38 @@ function accumulateEnterLeaveListenersForEvent(
635
if (capture) {
636
const captureListener = getListener(instance, registrationName);
637
if (captureListener != null) {
621
- // Capture listeners/instances should go at the start, so we
622
- // unshift them to the start of the array.
623
- dispatchListeners.unshift(captureListener);
624
- dispatchInstances.unshift(instance);
625
- dispatchCurrentTargets.unshift(currentTarget);
638
+ capturePhase.push(
639
+ createDispatchQueueItemPhaseEntry(
640
+ instance,
641
+ captureListener,
642
+ currentTarget,
643
+ ),
644
+ );
645
}
646
} else {
647
const bubbleListener = getListener(instance, registrationName);
648
if (bubbleListener != null) {
630
- // Bubble listeners/instances should go at the end, so we
631
- // push them to the end of the array.
632
- dispatchListeners.push(bubbleListener);
633
- dispatchInstances.push(instance);
634
- dispatchCurrentTargets.push(currentTarget);
649
+ bubblePhase.push(
650
+ createDispatchQueueItemPhaseEntry(
651
+ instance,
652
+ bubbleListener,
653
+ currentTarget,
654
+ ),
655
+ );
656
}
657
}
658
}
659
instance = instance.return;
660
}
640
- // To prevent allocation to the event unless we actually
641
- // have listeners we check the length of one of the arrays.
642
- if (dispatchListeners.length > 0) {
643
- event._dispatchListeners = dispatchListeners;
644
- event._dispatchInstances = dispatchInstances;
645
- event._dispatchCurrentTargets = dispatchCurrentTargets;
661
+ if (capturePhase.length !== 0 || bubblePhase.length !== 0) {
662
+ dispatchQueue.push(
663
+ createDispatchQueueItem(event, capturePhase, bubblePhase),
664
+ );
665
}
666
}
667
668
export function accumulateEnterLeaveListeners(
669
+ dispatchQueue: DispatchQueue,
670
leaveEvent: ReactSyntheticEvent,
671
enterEvent: ReactSyntheticEvent,
672
from: Fiber | null,
@@ -655,9 +675,21 @@ export function accumulateEnterLeaveListeners(
675
const common = from && to ? getLowestCommonAncestor(from, to) : null;
676
677
if (from !== null) {
658
- accumulateEnterLeaveListenersForEvent(leaveEvent, from, common, false);
678
+ accumulateEnterLeaveListenersForEvent(
679
+ dispatchQueue,
680
+ leaveEvent,
681
+ from,
682
+ common,
683
+ false,
684
+ );
685
}
686
if (to !== null) {
661
- accumulateEnterLeaveListenersForEvent(enterEvent, to, common, true);
687
+ accumulateEnterLeaveListenersForEvent(
688
+ dispatchQueue,
689
+ enterEvent,
690
+ to,
691
+ common,
692
+ true,
693
+ );
694
}
695
}
packages/react-dom/src/events/plugins/LegacySimpleEventPlugin.js
+2
-2
@@ -13,7 +13,7 @@ import type {
13
} from 'legacy-events/TopLevelEventTypes';
14
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
15
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
16
-import type {PluginModule} from 'legacy-events/PluginModuleType';
16
+import type {LegacyPluginModule} from 'legacy-events/PluginModuleType';
17
18
import SyntheticEvent from 'legacy-events/SyntheticEvent';
19
@@ -72,7 +72,7 @@ const knownHTMLTopLevelTypes: Array<DOMTopLevelEventType> = [
72
DOMTopLevelEventTypes.TOP_WAITING,
73
];
74
75
-const SimpleEventPlugin: PluginModule<MouseEvent> = {
75
+const SimpleEventPlugin: LegacyPluginModule<MouseEvent> = {
76
// simpleEventPluginEventTypes gets populated from
77
// the DOMEventProperties module.
78
eventTypes: simpleEventPluginEventTypes,
packages/react-dom/src/events/plugins/ModernBeforeInputEventPlugin.js
+12
-21
@@ -224,6 +224,7 @@ let isComposing = false;
224
* @return {?object} A SyntheticCompositionEvent.
225
*/
226
function extractCompositionEvent(
227
+ dispatchQueue,
228
topLevelType,
229
targetInst,
230
nativeEvent,
@@ -260,10 +261,11 @@ function extractCompositionEvent(
261
262
const event = SyntheticCompositionEvent.getPooled(
263
eventType,
263
- targetInst,
264
+ null,
265
nativeEvent,
266
nativeEventTarget,
267
);
268
+ accumulateTwoPhaseListeners(targetInst, dispatchQueue, event);
269
270
if (fallbackData) {
271
// Inject data generated from fallback path into the synthetic event.
@@ -275,9 +277,6 @@ function extractCompositionEvent(
277
event.data = customData;
278
}
279
}
278
-
279
- accumulateTwoPhaseListeners(event);
280
- return event;
280
}
281
282
/**
@@ -410,6 +409,7 @@ function getFallbackBeforeInputChars(topLevelType: TopLevelType, nativeEvent) {
409
* @return {?object} A SyntheticInputEvent.
410
*/
411
function extractBeforeInputEvent(
412
+ dispatchQueue,
413
topLevelType,
414
targetInst,
415
nativeEvent,
@@ -431,14 +431,12 @@ function extractBeforeInputEvent(
431
432
const event = SyntheticInputEvent.getPooled(
433
eventTypes.beforeInput,
434
- targetInst,
434
+ null,
435
nativeEvent,
436
nativeEventTarget,
437
);
438
-
438
+ accumulateTwoPhaseListeners(targetInst, dispatchQueue, event);
439
event.data = chars;
440
- accumulateTwoPhaseListeners(event);
441
- return event;
440
}
441
442
/**
@@ -463,35 +461,28 @@ const BeforeInputEventPlugin = {
461
eventTypes: eventTypes,
462
463
extractEvents: function(
464
+ dispatchQueue,
465
topLevelType,
466
targetInst,
467
nativeEvent,
468
nativeEventTarget,
469
eventSystemFlags,
470
+ container,
471
) {
472
- const composition = extractCompositionEvent(
472
+ extractCompositionEvent(
473
+ dispatchQueue,
474
topLevelType,
475
targetInst,
476
nativeEvent,
477
nativeEventTarget,
478
);
478
-
479
- const beforeInput = extractBeforeInputEvent(
479
+ extractBeforeInputEvent(
480
+ dispatchQueue,
481
topLevelType,
482
targetInst,
483
nativeEvent,
484
nativeEventTarget,
485
);
485
-
486
- if (composition === null) {
487
- return beforeInput;
488
- }
489
-
490
- if (beforeInput === null) {
491
- return composition;
492
- }
493
-
494
- return [composition, beforeInput];
486
},
487
};
488
packages/react-dom/src/events/plugins/ModernChangeEventPlugin.js
+13
-5
@@ -52,17 +52,22 @@ const eventTypes = {
52
},
53
};
54
55
-function createAndAccumulateChangeEvent(inst, nativeEvent, target) {
55
+function createAndAccumulateChangeEvent(
56
+ dispatchQueue,
57
+ inst,
58
+ nativeEvent,
59
+ target,
60
+) {
61
const event = SyntheticEvent.getPooled(
62
eventTypes.change,
58
- inst,
63
+ null,
64
nativeEvent,
65
target,
66
);
67
event.type = 'change';
68
// Flag this event loop as needing state restore.
69
enqueueStateRestore(target);
65
- accumulateTwoPhaseListeners(event);
70
+ accumulateTwoPhaseListeners(inst, dispatchQueue, event);
71
return event;
72
}
73
/**
@@ -264,11 +269,13 @@ const ChangeEventPlugin = {
269
_isInputEventSupported: isInputEventSupported,
270
271
extractEvents: function(
272
+ dispatchQueue,
273
topLevelType,
274
targetInst,
275
nativeEvent,
276
nativeEventTarget,
277
eventSystemFlags,
278
+ container,
279
) {
280
const targetNode = targetInst ? getNodeFromInstance(targetInst) : window;
281
@@ -289,12 +296,13 @@ const ChangeEventPlugin = {
296
if (getTargetInstFunc) {
297
const inst = getTargetInstFunc(topLevelType, targetInst);
298
if (inst) {
292
- const event = createAndAccumulateChangeEvent(
299
+ createAndAccumulateChangeEvent(
300
+ dispatchQueue,
301
inst,
302
nativeEvent,
303
nativeEventTarget,
304
);
297
- return event;
305
+ return;
306
}
307
}
308
packages/react-dom/src/events/plugins/ModernEnterLeaveEventPlugin.js
+6
-6
@@ -53,11 +53,13 @@ const EnterLeaveEventPlugin = {
53
* the `mouseover` top-level event.
54
*/
55
extractEvents: function(
56
+ dispatchQueue,
57
topLevelType,
58
targetInst,
59
nativeEvent,
60
nativeEventTarget,
61
eventSystemFlags,
62
+ container,
63
) {
64
const isOverEvent =
65
topLevelType === TOP_MOUSE_OVER || topLevelType === TOP_POINTER_OVER;
@@ -73,14 +75,14 @@ const EnterLeaveEventPlugin = {
75
// make sure the node that we're coming from is managed by React.
76
const inst = getClosestInstanceFromNode(related);
77
if (inst !== null) {
76
- return null;
78
+ return;
79
}
80
}
81
}
82
83
if (!isOutEvent && !isOverEvent) {
84
// Must not be a mouse or pointer in or out - ignoring.
83
- return null;
85
+ return;
86
}
87
88
let win;
@@ -120,7 +122,7 @@ const EnterLeaveEventPlugin = {
122
123
if (from === to) {
124
// Nothing pertains to our managed components.
123
- return null;
125
+ return;
126
}
127
128
let eventInterface, leaveEventType, enterEventType, eventTypePrefix;
@@ -163,9 +165,7 @@ const EnterLeaveEventPlugin = {
165
enter.target = toNode;
166
enter.relatedTarget = fromNode;
167
166
- accumulateEnterLeaveListeners(leave, enter, from, to);
167
-
168
- return [leave, enter];
168
+ accumulateEnterLeaveListeners(dispatchQueue, leave, enter, from, to);
169
},
170
};
171
packages/react-dom/src/events/plugins/ModernSelectEventPlugin.js
+14
-12
@@ -108,7 +108,7 @@ function getEventTargetDocument(eventTarget) {
108
* @param {object} nativeEventTarget
109
* @return {?SyntheticEvent}
110
*/
111
-function constructSelectEvent(nativeEvent, nativeEventTarget) {
111
+function constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) {
112
// Ensure we have the right element, and that the user is not dragging a
113
// selection (this matches native `select` event behavior). In HTML5, select
114
// fires only on input and textarea thus if there's no focused element we
@@ -120,7 +120,7 @@ function constructSelectEvent(nativeEvent, nativeEventTarget) {
120
activeElement == null ||
121
activeElement !== getActiveElement(doc)
122
) {
123
- return null;
123
+ return;
124
}
125
126
// Only fire when selection has actually changed.
@@ -130,7 +130,7 @@ function constructSelectEvent(nativeEvent, nativeEventTarget) {
130
131
const syntheticEvent = SyntheticEvent.getPooled(
132
eventTypes.select,
133
- activeElementInst,
133
+ null,
134
nativeEvent,
135
nativeEventTarget,
136
);
@@ -138,12 +138,12 @@ function constructSelectEvent(nativeEvent, nativeEventTarget) {
138
syntheticEvent.type = 'select';
139
syntheticEvent.target = activeElement;
140
141
- accumulateTwoPhaseListeners(syntheticEvent);
142
-
143
- return syntheticEvent;
141
+ accumulateTwoPhaseListeners(
142
+ activeElementInst,
143
+ dispatchQueue,
144
+ syntheticEvent,
145
+ );
146
}
145
-
146
- return null;
147
}
148
149
function isListeningToEvents(
@@ -186,6 +186,7 @@ const SelectEventPlugin = {
186
eventTypes: eventTypes,
187
188
extractEvents: function(
189
+ dispatchQueue,
190
topLevelType,
191
targetInst,
192
nativeEvent,
@@ -207,7 +208,7 @@ const SelectEventPlugin = {
208
(topLevelType !== TOP_SELECTION_CHANGE &&
209
!isListeningToEvents(rootTargetDependencies, container))
210
) {
210
- return null;
211
+ return;
212
}
213
214
const targetNode = targetInst ? getNodeFromInstance(targetInst) : window;
@@ -238,7 +239,8 @@ const SelectEventPlugin = {
239
case TOP_MOUSE_UP:
240
case TOP_DRAG_END:
241
mouseDown = false;
241
- return constructSelectEvent(nativeEvent, nativeEventTarget);
242
+ constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget);
243
+ break;
244
// Chrome and IE fire non-standard event when selection is changed (and
245
// sometimes when it hasn't). IE's event fires out of order with respect
246
// to key and input events on deletion, so we discard it.
@@ -255,10 +257,10 @@ const SelectEventPlugin = {
257
// falls through
258
case TOP_KEY_DOWN:
259
case TOP_KEY_UP:
258
- return constructSelectEvent(nativeEvent, nativeEventTarget);
260
+ constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget);
261
}
262
261
- return null;
263
+ return;
264
},
265
};
266
packages/react-dom/src/events/plugins/ModernSimpleEventPlugin.js
+14
-12
@@ -11,9 +11,11 @@ import type {
11
TopLevelType,
12
DOMTopLevelEventType,
13
} from 'legacy-events/TopLevelEventTypes';
14
-import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
14
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
16
-import type {PluginModule} from 'legacy-events/PluginModuleType';
15
+import type {
16
+ ModernPluginModule,
17
+ DispatchQueue,
18
+} from 'legacy-events/PluginModuleType';
19
import type {EventSystemFlags} from '../EventSystemFlags';
20
21
import SyntheticEvent from 'legacy-events/SyntheticEvent';
@@ -73,21 +75,22 @@ const knownHTMLTopLevelTypes: Array<DOMTopLevelEventType> = [
75
DOMTopLevelEventTypes.TOP_WAITING,
76
];
77
76
-const SimpleEventPlugin: PluginModule<MouseEvent> = {
78
+const SimpleEventPlugin: ModernPluginModule<MouseEvent> = {
79
// simpleEventPluginEventTypes gets populated from
80
// the DOMEventProperties module.
81
eventTypes: simpleEventPluginEventTypes,
82
extractEvents: function(
83
+ dispatchQueue: DispatchQueue,
84
topLevelType: TopLevelType,
85
targetInst: null | Fiber,
86
nativeEvent: MouseEvent,
87
nativeEventTarget: null | EventTarget,
85
- eventSystemFlags?: EventSystemFlags,
86
- targetContainer?: null | EventTarget,
87
- ): null | ReactSyntheticEvent {
88
+ eventSystemFlags: EventSystemFlags,
89
+ targetContainer: null | EventTarget,
90
+ ): void {
91
const dispatchConfig = topLevelEventsToDispatchConfig.get(topLevelType);
92
if (!dispatchConfig) {
90
- return null;
93
+ return;
94
}
95
let EventConstructor;
96
switch (topLevelType) {
@@ -96,7 +99,7 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
99
// the unwanted keypress events. Enter is however both printable and
100
// non-printable. One would expect Tab to be as well (but it isn't).
101
if (getEventCharCode(nativeEvent) === 0) {
99
- return null;
102
+ return;
103
}
104
/* falls through */
105
case DOMTopLevelEventTypes.TOP_KEY_DOWN:
@@ -113,7 +116,7 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
116
// Firefox creates a click event on right mouse clicks. This removes the
117
// unwanted click events.
118
if (nativeEvent.button === 2) {
116
- return null;
119
+ return;
120
}
121
/* falls through */
122
case DOMTopLevelEventTypes.TOP_AUX_CLICK:
@@ -193,13 +196,12 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
196
}
197
const event = EventConstructor.getPooled(
198
dispatchConfig,
196
- targetInst,
199
+ null,
200
nativeEvent,
201
nativeEventTarget,
202
);
203
201
- accumulateTwoPhaseListeners(event);
202
- return event;
204
+ accumulateTwoPhaseListeners(targetInst, dispatchQueue, event);
205
},
206
};
207
packages/react-native-renderer/src/ReactFabricEventEmitter.js
+4
-3
@@ -9,7 +9,7 @@
9
10
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
11
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
12
-import type {PluginModule} from 'legacy-events/PluginModuleType';
12
+import type {LegacyPluginModule} from 'legacy-events/PluginModuleType';
13
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
14
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
15
@@ -37,9 +37,10 @@ function extractPluginEvents(
37
nativeEventTarget: null | EventTarget,
38
): Array<ReactSyntheticEvent> | ReactSyntheticEvent | null {
39
let events = null;
40
- for (let i = 0; i < plugins.length; i++) {
40
+ const legacyPlugins = ((plugins: any): Array<LegacyPluginModule<Event>>);
41
+ for (let i = 0; i < legacyPlugins.length; i++) {
42
// Not every plugin in the ordering may be loaded at runtime.
42
- const possiblePlugin: PluginModule<AnyNativeEvent> = plugins[i];
43
+ const possiblePlugin: LegacyPluginModule<AnyNativeEvent> = legacyPlugins[i];
44
if (possiblePlugin) {
45
const extractedEvents = possiblePlugin.extractEvents(
46
topLevelType,
packages/react-native-renderer/src/ReactNativeEventEmitter.js
+4
-3
@@ -9,7 +9,7 @@
9
10
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
11
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
12
-import type {PluginModule} from 'legacy-events/PluginModuleType';
12
+import type {LegacyPluginModule} from 'legacy-events/PluginModuleType';
13
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
14
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
15
@@ -126,9 +126,10 @@ function extractPluginEvents(
126
nativeEventTarget: null | EventTarget,
127
): Array<ReactSyntheticEvent> | ReactSyntheticEvent | null {
128
let events = null;
129
- for (let i = 0; i < plugins.length; i++) {
129
+ const legacyPlugins = ((plugins: any): Array<LegacyPluginModule<Event>>);
130
+ for (let i = 0; i < legacyPlugins.length; i++) {
131
// Not every plugin in the ordering may be loaded at runtime.
131
- const possiblePlugin: PluginModule<AnyNativeEvent> = plugins[i];
132
+ const possiblePlugin: LegacyPluginModule<AnyNativeEvent> = legacyPlugins[i];
133
if (possiblePlugin) {
134
const extractedEvents = possiblePlugin.extractEvents(
135
topLevelType,