@samitouri / QOS-React-2 / commits / 9751935abf

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,