@samitouri / QOS-React / commits / 62861bbcc7

More event system cleanup and scaffolding (#18179)

Dominic Gannaway committed Mar 2, 2020 at 10:59 UTC 62861bbcc752c63728592fdc45078854f9aae161
10 files changed +396 -134
packages/legacy-events/PluginModuleType.js
+1 -1
@@ -29,7 +29,7 @@ export type PluginModule<NativeEvent> = {
29 nativeTarget: NativeEvent,
30 nativeEventTarget: null | EventTarget,
31 eventSystemFlags: EventSystemFlags,
32 - container?: Document | Element | Node,
32 + container?: Document | Element,
33 ) => ?ReactSyntheticEvent,
34 tapMoveThreshold?: number,
35 };
packages/react-dom/src/client/ReactDOMComponent.js
+103 -37
@@ -10,6 +10,7 @@
10 import {registrationNameModules} from 'legacy-events/EventPluginRegistry';
11 import {canUseDOM} from 'shared/ExecutionEnvironment';
12 import endsWith from 'shared/endsWith';
13 +import invariant from 'shared/invariant';
14 import {setListenToResponderEventTypes} from '../events/DeprecatedDOMEventResponderSystem';
15
16 import {
@@ -59,7 +60,6 @@ import {getListenerMapForElement} from '../events/DOMEventListenerMap';
60 import {
61 addResponderEventSystemEvent,
62 removeActiveResponderEventSystemEvent,
62 - trapBubbledEvent,
63 } from '../events/ReactDOMEventListener.js';
64 import {mediaEventTypes} from '../events/DOMTopLevelEventTypes';
65 import {
@@ -74,7 +74,12 @@ import {
74 shouldRemoveAttribute,
75 } from '../shared/DOMProperty';
76 import assertValidProps from '../shared/assertValidProps';
77 -import {DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE} from '../shared/HTMLNodeType';
77 +import {
78 + DOCUMENT_NODE,
79 + DOCUMENT_FRAGMENT_NODE,
80 + ELEMENT_NODE,
81 + COMMENT_NODE,
82 +} from '../shared/HTMLNodeType';
83 import isCustomComponent from '../shared/isCustomComponent';
84 import possibleStandardNames from '../shared/possibleStandardNames';
85 import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook';
@@ -84,8 +89,13 @@ import {validateProperties as validateUnknownProperties} from '../shared/ReactDO
89 import {
90 enableDeprecatedFlareAPI,
91 enableTrustedTypesIntegration,
92 + enableModernEventSystem,
93 } from 'shared/ReactFeatureFlags';
88 -import {legacyListenToEvent} from '../events/DOMLegacyEventPluginSystem';
94 +import {
95 + legacyListenToEvent,
96 + legacyTrapBubbledEvent,
97 +} from '../events/DOMLegacyEventPluginSystem';
98 +import {listenToEvent} from '../events/DOMModernPluginEventSystem';
99
100 let didWarnInvalidHydration = false;
101 let didWarnScriptTags = false;
@@ -260,16 +270,36 @@ if (__DEV__) {
270 }
271
272 function ensureListeningTo(
263 - rootContainerElement: Element | Node,
273 + rootContainerInstance: Element | Node,
274 registrationName: string,
275 ): void {
266 - const isDocumentOrFragment =
267 - rootContainerElement.nodeType === DOCUMENT_NODE ||
268 - rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE;
269 - const doc = isDocumentOrFragment
270 - ? rootContainerElement
271 - : rootContainerElement.ownerDocument;
272 - legacyListenToEvent(registrationName, doc);
276 + if (enableModernEventSystem) {
277 + // If we have a comment node, then use the parent node,
278 + // which should be an element.
279 + const rootContainerElement =
280 + rootContainerInstance.nodeType === COMMENT_NODE
281 + ? rootContainerInstance.parentNode
282 + : rootContainerInstance;
283 + // Containers can only ever be element nodes. We do not
284 + // want to register events to document fragments or documents
285 + // with the modern plugin event system.
286 + invariant(
287 + rootContainerElement != null &&
288 + rootContainerElement.nodeType === ELEMENT_NODE,
289 + 'ensureListeningTo(): received a container that was not an element node. ' +
290 + 'This is likely a bug in React.',
291 + );
292 + listenToEvent(registrationName, ((rootContainerElement: any): Element));
293 + } else {
294 + // Legacy plugin event system path
295 + const isDocumentOrFragment =
296 + rootContainerInstance.nodeType === DOCUMENT_NODE ||
297 + rootContainerInstance.nodeType === DOCUMENT_FRAGMENT_NODE;
298 + const doc = isDocumentOrFragment
299 + ? rootContainerInstance
300 + : rootContainerInstance.ownerDocument;
301 + legacyListenToEvent(registrationName, ((doc: any): Document));
302 + }
303 }
304
305 function getOwnerDocumentFromRootContainer(
@@ -514,41 +544,55 @@ export function setInitialProperties(
544 case 'iframe':
545 case 'object':
546 case 'embed':
517 - trapBubbledEvent(TOP_LOAD, domElement);
547 + if (!enableModernEventSystem) {
548 + legacyTrapBubbledEvent(TOP_LOAD, domElement);
549 + }
550 props = rawProps;
551 break;
552 case 'video':
553 case 'audio':
522 - // Create listener for each media event
523 - for (let i = 0; i < mediaEventTypes.length; i++) {
524 - trapBubbledEvent(mediaEventTypes[i], domElement);
554 + if (!enableModernEventSystem) {
555 + // Create listener for each media event
556 + for (let i = 0; i < mediaEventTypes.length; i++) {
557 + legacyTrapBubbledEvent(mediaEventTypes[i], domElement);
558 + }
559 }
560 props = rawProps;
561 break;
562 case 'source':
529 - trapBubbledEvent(TOP_ERROR, domElement);
563 + if (!enableModernEventSystem) {
564 + legacyTrapBubbledEvent(TOP_ERROR, domElement);
565 + }
566 props = rawProps;
567 break;
568 case 'img':
569 case 'image':
570 case 'link':
535 - trapBubbledEvent(TOP_ERROR, domElement);
536 - trapBubbledEvent(TOP_LOAD, domElement);
571 + if (!enableModernEventSystem) {
572 + legacyTrapBubbledEvent(TOP_ERROR, domElement);
573 + legacyTrapBubbledEvent(TOP_LOAD, domElement);
574 + }
575 props = rawProps;
576 break;
577 case 'form':
540 - trapBubbledEvent(TOP_RESET, domElement);
541 - trapBubbledEvent(TOP_SUBMIT, domElement);
578 + if (!enableModernEventSystem) {
579 + legacyTrapBubbledEvent(TOP_RESET, domElement);
580 + legacyTrapBubbledEvent(TOP_SUBMIT, domElement);
581 + }
582 props = rawProps;
583 break;
584 case 'details':
545 - trapBubbledEvent(TOP_TOGGLE, domElement);
585 + if (!enableModernEventSystem) {
586 + legacyTrapBubbledEvent(TOP_TOGGLE, domElement);
587 + }
588 props = rawProps;
589 break;
590 case 'input':
591 ReactDOMInputInitWrapperState(domElement, rawProps);
592 props = ReactDOMInputGetHostProps(domElement, rawProps);
551 - trapBubbledEvent(TOP_INVALID, domElement);
593 + if (!enableModernEventSystem) {
594 + legacyTrapBubbledEvent(TOP_INVALID, domElement);
595 + }
596 // For controlled components we always need to ensure we're listening
597 // to onChange. Even if there is no listener.
598 ensureListeningTo(rootContainerElement, 'onChange');
@@ -560,7 +604,9 @@ export function setInitialProperties(
604 case 'select':
605 ReactDOMSelectInitWrapperState(domElement, rawProps);
606 props = ReactDOMSelectGetHostProps(domElement, rawProps);
563 - trapBubbledEvent(TOP_INVALID, domElement);
607 + if (!enableModernEventSystem) {
608 + legacyTrapBubbledEvent(TOP_INVALID, domElement);
609 + }
610 // For controlled components we always need to ensure we're listening
611 // to onChange. Even if there is no listener.
612 ensureListeningTo(rootContainerElement, 'onChange');
@@ -568,7 +614,9 @@ export function setInitialProperties(
614 case 'textarea':
615 ReactDOMTextareaInitWrapperState(domElement, rawProps);
616 props = ReactDOMTextareaGetHostProps(domElement, rawProps);
571 - trapBubbledEvent(TOP_INVALID, domElement);
617 + if (!enableModernEventSystem) {
618 + legacyTrapBubbledEvent(TOP_INVALID, domElement);
619 + }
620 // For controlled components we always need to ensure we're listening
621 // to onChange. Even if there is no listener.
622 ensureListeningTo(rootContainerElement, 'onChange');
@@ -898,34 +946,48 @@ export function diffHydratedProperties(
946 case 'iframe':
947 case 'object':
948 case 'embed':
901 - trapBubbledEvent(TOP_LOAD, domElement);
949 + if (!enableModernEventSystem) {
950 + legacyTrapBubbledEvent(TOP_LOAD, domElement);
951 + }
952 break;
953 case 'video':
954 case 'audio':
905 - // Create listener for each media event
906 - for (let i = 0; i < mediaEventTypes.length; i++) {
907 - trapBubbledEvent(mediaEventTypes[i], domElement);
955 + if (!enableModernEventSystem) {
956 + // Create listener for each media event
957 + for (let i = 0; i < mediaEventTypes.length; i++) {
958 + legacyTrapBubbledEvent(mediaEventTypes[i], domElement);
959 + }
960 }
961 break;
962 case 'source':
911 - trapBubbledEvent(TOP_ERROR, domElement);
963 + if (!enableModernEventSystem) {
964 + legacyTrapBubbledEvent(TOP_ERROR, domElement);
965 + }
966 break;
967 case 'img':
968 case 'image':
969 case 'link':
916 - trapBubbledEvent(TOP_ERROR, domElement);
917 - trapBubbledEvent(TOP_LOAD, domElement);
970 + if (!enableModernEventSystem) {
971 + legacyTrapBubbledEvent(TOP_ERROR, domElement);
972 + legacyTrapBubbledEvent(TOP_LOAD, domElement);
973 + }
974 break;
975 case 'form':
920 - trapBubbledEvent(TOP_RESET, domElement);
921 - trapBubbledEvent(TOP_SUBMIT, domElement);
976 + if (!enableModernEventSystem) {
977 + legacyTrapBubbledEvent(TOP_RESET, domElement);
978 + legacyTrapBubbledEvent(TOP_SUBMIT, domElement);
979 + }
980 break;
981 case 'details':
924 - trapBubbledEvent(TOP_TOGGLE, domElement);
982 + if (!enableModernEventSystem) {
983 + legacyTrapBubbledEvent(TOP_TOGGLE, domElement);
984 + }
985 break;
986 case 'input':
987 ReactDOMInputInitWrapperState(domElement, rawProps);
928 - trapBubbledEvent(TOP_INVALID, domElement);
988 + if (!enableModernEventSystem) {
989 + legacyTrapBubbledEvent(TOP_INVALID, domElement);
990 + }
991 // For controlled components we always need to ensure we're listening
992 // to onChange. Even if there is no listener.
993 ensureListeningTo(rootContainerElement, 'onChange');
@@ -935,14 +997,18 @@ export function diffHydratedProperties(
997 break;
998 case 'select':
999 ReactDOMSelectInitWrapperState(domElement, rawProps);
938 - trapBubbledEvent(TOP_INVALID, domElement);
1000 + if (!enableModernEventSystem) {
1001 + legacyTrapBubbledEvent(TOP_INVALID, domElement);
1002 + }
1003 // For controlled components we always need to ensure we're listening
1004 // to onChange. Even if there is no listener.
1005 ensureListeningTo(rootContainerElement, 'onChange');
1006 break;
1007 case 'textarea':
1008 ReactDOMTextareaInitWrapperState(domElement, rawProps);
945 - trapBubbledEvent(TOP_INVALID, domElement);
1009 + if (!enableModernEventSystem) {
1010 + legacyTrapBubbledEvent(TOP_INVALID, domElement);
1011 + }
1012 // For controlled components we always need to ensure we're listening
1013 // to onChange. Even if there is no listener.
1014 ensureListeningTo(rootContainerElement, 'onChange');
packages/react-dom/src/events/DOMEventListenerMap.js
+18
@@ -9,6 +9,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 // prettier-ignore
16 const elementListenerMap:
@@ -29,3 +31,19 @@ export function getListenerMapForElement(
31 }
32 return listenerMap;
33 }
34 +
35 +export function isListeningToAllDependencies(
36 + registrationName: string,
37 + mountAt: Document | Element,
38 +): boolean {
39 + const listenerMap = getListenerMapForElement(mountAt);
40 + const dependencies = registrationNameDependencies[registrationName];
41 +
42 + for (let i = 0; i < dependencies.length; i++) {
43 + const dependency = dependencies[i];
44 + if (!listenerMap.has(dependency)) {
45 + return false;
46 + }
47 + }
48 + return true;
49 +}
packages/react-dom/src/events/DOMLegacyEventPluginSystem.js
+19 -21
@@ -25,7 +25,6 @@ import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
25
26 import getEventTarget from './getEventTarget';
27 import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
28 -import {trapCapturedEvent, trapBubbledEvent} from './ReactDOMEventListener';
28 import {getListenerMapForElement} from './DOMEventListenerMap';
29 import isEventSupported from './isEventSupported';
30 import {
@@ -40,6 +39,7 @@ import {
39 getRawEventName,
40 mediaEventTypes,
41 } from './DOMTopLevelEventTypes';
42 +import {trapEventForPluginEventSystem} from './ReactDOMEventListener';
43
44 /**
45 * Summary of `DOMEventPluginSystem` event handling:
@@ -309,7 +309,7 @@ export function dispatchEventForLegacyPluginEventSystem(
309 */
310 export function legacyListenToEvent(
311 registrationName: string,
312 - mountAt: Document | Element | Node,
312 + mountAt: Document | Element,
313 ): void {
314 const listenerMap = getListenerMapForElement(mountAt);
315 const dependencies = registrationNameDependencies[registrationName];
@@ -322,18 +322,18 @@ export function legacyListenToEvent(
322
323 export function legacyListenToTopLevelEvent(
324 topLevelType: DOMTopLevelEventType,
325 - mountAt: Document | Element | Node,
325 + mountAt: Document | Element,
326 listenerMap: Map<DOMTopLevelEventType | string, null | (any => void)>,
327 ): void {
328 if (!listenerMap.has(topLevelType)) {
329 switch (topLevelType) {
330 case TOP_SCROLL:
331 - trapCapturedEvent(TOP_SCROLL, mountAt);
331 + legacyTrapCapturedEvent(TOP_SCROLL, mountAt);
332 break;
333 case TOP_FOCUS:
334 case TOP_BLUR:
335 - trapCapturedEvent(TOP_FOCUS, mountAt);
336 - trapCapturedEvent(TOP_BLUR, mountAt);
335 + legacyTrapCapturedEvent(TOP_FOCUS, mountAt);
336 + legacyTrapCapturedEvent(TOP_BLUR, mountAt);
337 // We set the flag for a single dependency later in this function,
338 // but this ensures we mark both as attached rather than just one.
339 listenerMap.set(TOP_BLUR, null);
@@ -342,7 +342,7 @@ export function legacyListenToTopLevelEvent(
342 case TOP_CANCEL:
343 case TOP_CLOSE:
344 if (isEventSupported(getRawEventName(topLevelType))) {
345 - trapCapturedEvent(topLevelType, mountAt);
345 + legacyTrapCapturedEvent(topLevelType, mountAt);
346 }
347 break;
348 case TOP_INVALID:
@@ -356,7 +356,7 @@ export function legacyListenToTopLevelEvent(
356 // Media events don't bubble so adding the listener wouldn't do anything.
357 const isMediaEvent = mediaEventTypes.indexOf(topLevelType) !== -1;
358 if (!isMediaEvent) {
359 - trapBubbledEvent(topLevelType, mountAt);
359 + legacyTrapBubbledEvent(topLevelType, mountAt);
360 }
361 break;
362 }
@@ -364,18 +364,16 @@ export function legacyListenToTopLevelEvent(
364 }
365 }
366
367 -export function isListeningToAllDependencies(
368 - registrationName: string,
369 - mountAt: Document | Element,
370 -): boolean {
371 - const listenerMap = getListenerMapForElement(mountAt);
372 - const dependencies = registrationNameDependencies[registrationName];
367 +export function legacyTrapBubbledEvent(
368 + topLevelType: DOMTopLevelEventType,
369 + element: Document | Element,
370 +): void {
371 + trapEventForPluginEventSystem(element, topLevelType, false);
372 +}
373
374 - for (let i = 0; i < dependencies.length; i++) {
375 - const dependency = dependencies[i];
376 - if (!listenerMap.has(dependency)) {
377 - return false;
378 - }
379 - }
380 - return true;
374 +export function legacyTrapCapturedEvent(
375 + topLevelType: DOMTopLevelEventType,
376 + element: Document | Element,
377 +): void {
378 + trapEventForPluginEventSystem(element, topLevelType, true);
379 }
packages/react-dom/src/events/DOMModernPluginEventSystem.js new
+127
@@ -0,0 +1,127 @@
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 {AnyNativeEvent} from 'legacy-events/PluginModuleType';
11 +import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
12 +import type {EventSystemFlags} from 'legacy-events/EventSystemFlags';
13 +import type {Fiber} from 'react-reconciler/src/ReactFiber';
14 +
15 +import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
16 +
17 +import {trapEventForPluginEventSystem} from './ReactDOMEventListener';
18 +import {getListenerMapForElement} from './DOMEventListenerMap';
19 +import {
20 + TOP_FOCUS,
21 + TOP_LOAD,
22 + TOP_ABORT,
23 + TOP_CANCEL,
24 + TOP_INVALID,
25 + TOP_BLUR,
26 + TOP_SCROLL,
27 + TOP_CLOSE,
28 + TOP_RESET,
29 + TOP_SUBMIT,
30 + TOP_CAN_PLAY,
31 + TOP_CAN_PLAY_THROUGH,
32 + TOP_DURATION_CHANGE,
33 + TOP_EMPTIED,
34 + TOP_ENCRYPTED,
35 + TOP_ENDED,
36 + TOP_ERROR,
37 + TOP_WAITING,
38 + TOP_VOLUME_CHANGE,
39 + TOP_TIME_UPDATE,
40 + TOP_SUSPEND,
41 + TOP_STALLED,
42 + TOP_SEEKING,
43 + TOP_SEEKED,
44 + TOP_PLAY,
45 + TOP_PAUSE,
46 + TOP_LOAD_START,
47 + TOP_LOADED_DATA,
48 + TOP_LOADED_METADATA,
49 + TOP_RATE_CHANGE,
50 + TOP_PROGRESS,
51 + TOP_PLAYING,
52 +} from './DOMTopLevelEventTypes';
53 +
54 +const capturePhaseEvents = new Set([
55 + TOP_FOCUS,
56 + TOP_BLUR,
57 + TOP_SCROLL,
58 + TOP_LOAD,
59 + TOP_ABORT,
60 + TOP_CANCEL,
61 + TOP_CLOSE,
62 + TOP_INVALID,
63 + TOP_RESET,
64 + TOP_SUBMIT,
65 + TOP_ABORT,
66 + TOP_CAN_PLAY,
67 + TOP_CAN_PLAY_THROUGH,
68 + TOP_DURATION_CHANGE,
69 + TOP_EMPTIED,
70 + TOP_ENCRYPTED,
71 + TOP_ENDED,
72 + TOP_ERROR,
73 + TOP_LOADED_DATA,
74 + TOP_LOADED_METADATA,
75 + TOP_LOAD_START,
76 + TOP_PAUSE,
77 + TOP_PLAY,
78 + TOP_PLAYING,
79 + TOP_PROGRESS,
80 + TOP_RATE_CHANGE,
81 + TOP_SEEKED,
82 + TOP_SEEKING,
83 + TOP_STALLED,
84 + TOP_SUSPEND,
85 + TOP_TIME_UPDATE,
86 + TOP_VOLUME_CHANGE,
87 + TOP_WAITING,
88 +]);
89 +
90 +export function listenToTopLevelEvent(
91 + topLevelType: DOMTopLevelEventType,
92 + rootContainerElement: Element,
93 + listenerMap: Map<DOMTopLevelEventType | string, null | (any => void)>,
94 +): void {
95 + if (!listenerMap.has(topLevelType)) {
96 + const isCapturePhase = capturePhaseEvents.has(topLevelType);
97 + trapEventForPluginEventSystem(
98 + rootContainerElement,
99 + topLevelType,
100 + isCapturePhase,
101 + );
102 + listenerMap.set(topLevelType, null);
103 + }
104 +}
105 +
106 +export function listenToEvent(
107 + registrationName: string,
108 + rootContainerElement: Element,
109 +): void {
110 + const listenerMap = getListenerMapForElement(rootContainerElement);
111 + const dependencies = registrationNameDependencies[registrationName];
112 +
113 + for (let i = 0; i < dependencies.length; i++) {
114 + const dependency = dependencies[i];
115 + listenToTopLevelEvent(dependency, rootContainerElement, listenerMap);
116 + }
117 +}
118 +
119 +export function dispatchEventForPluginEventSystem(
120 + topLevelType: DOMTopLevelEventType,
121 + eventSystemFlags: EventSystemFlags,
122 + nativeEvent: AnyNativeEvent,
123 + targetInst: null | Fiber,
124 + rootContainer: Document | Element,
125 +): void {
126 + // TODO
127 +}
packages/react-dom/src/events/ReactDOMEventListener.js
+83 -61
@@ -53,7 +53,10 @@ import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
53 import {getRawEventName} from './DOMTopLevelEventTypes';
54 import {passiveBrowserEventsSupported} from './checkPassiveEvents';
55
56 -import {enableDeprecatedFlareAPI} from 'shared/ReactFeatureFlags';
56 +import {
57 + enableDeprecatedFlareAPI,
58 + enableModernEventSystem,
59 +} from 'shared/ReactFeatureFlags';
60 import {
61 UserBlockingEvent,
62 ContinuousEvent,
@@ -61,6 +64,7 @@ import {
64 } from 'shared/ReactTypes';
65 import {getEventPriorityForPluginSystem} from './DOMEventProperties';
66 import {dispatchEventForLegacyPluginEventSystem} from './DOMLegacyEventPluginSystem';
67 +import {dispatchEventForPluginEventSystem} from './DOMModernPluginEventSystem';
68
69 const {
70 unstable_UserBlockingPriority: UserBlockingPriority,
@@ -78,20 +82,6 @@ export function isEnabled() {
82 return _enabled;
83 }
84
81 -export function trapBubbledEvent(
82 - topLevelType: DOMTopLevelEventType,
83 - element: Document | Element | Node,
84 -): void {
85 - trapEventForPluginEventSystem(element, topLevelType, false);
86 -}
87 -
88 -export function trapCapturedEvent(
89 - topLevelType: DOMTopLevelEventType,
90 - element: Document | Element | Node,
91 -): void {
92 - trapEventForPluginEventSystem(element, topLevelType, true);
93 -}
94 -
85 export function addResponderEventSystemEvent(
86 document: Document,
87 topLevelType: string,
@@ -149,39 +139,31 @@ export function removeActiveResponderEventSystemEvent(
139 }
140 }
141
152 -function trapEventForPluginEventSystem(
153 - container: Document | Element | Node,
142 +export function trapEventForPluginEventSystem(
143 + container: Document | Element,
144 topLevelType: DOMTopLevelEventType,
145 capture: boolean,
146 ): void {
147 let listener;
148 + let listenerWrapper;
149 switch (getEventPriorityForPluginSystem(topLevelType)) {
150 case DiscreteEvent:
160 - listener = dispatchDiscreteEvent.bind(
161 - null,
162 - topLevelType,
163 - PLUGIN_EVENT_SYSTEM,
164 - container,
165 - );
151 + listenerWrapper = dispatchDiscreteEvent;
152 break;
153 case UserBlockingEvent:
168 - listener = dispatchUserBlockingUpdate.bind(
169 - null,
170 - topLevelType,
171 - PLUGIN_EVENT_SYSTEM,
172 - container,
173 - );
154 + listenerWrapper = dispatchUserBlockingUpdate;
155 break;
156 case ContinuousEvent:
157 default:
177 - listener = dispatchEvent.bind(
178 - null,
179 - topLevelType,
180 - PLUGIN_EVENT_SYSTEM,
181 - container,
182 - );
158 + listenerWrapper = dispatchEvent;
159 break;
160 }
161 + listener = listenerWrapper.bind(
162 + null,
163 + topLevelType,
164 + PLUGIN_EVENT_SYSTEM,
165 + container,
166 + );
167
168 const rawEventName = getRawEventName(topLevelType);
169 if (capture) {
@@ -228,7 +210,7 @@ function dispatchUserBlockingUpdate(
210 export function dispatchEvent(
211 topLevelType: DOMTopLevelEventType,
212 eventSystemFlags: EventSystemFlags,
231 - container: Document | Element | Node,
213 + container: Document | Element,
214 nativeEvent: AnyNativeEvent,
215 ): void {
216 if (!_enabled) {
@@ -293,12 +275,22 @@ export function dispatchEvent(
275 // in case the event system needs to trace it.
276 if (enableDeprecatedFlareAPI) {
277 if (eventSystemFlags & PLUGIN_EVENT_SYSTEM) {
296 - dispatchEventForLegacyPluginEventSystem(
297 - topLevelType,
298 - eventSystemFlags,
299 - nativeEvent,
300 - null,
301 - );
278 + if (enableModernEventSystem) {
279 + dispatchEventForPluginEventSystem(
280 + topLevelType,
281 + eventSystemFlags,
282 + nativeEvent,
283 + null,
284 + container,
285 + );
286 + } else {
287 + dispatchEventForLegacyPluginEventSystem(
288 + topLevelType,
289 + eventSystemFlags,
290 + nativeEvent,
291 + null,
292 + );
293 + }
294 }
295 if (eventSystemFlags & RESPONDER_EVENT_SYSTEM) {
296 // React Flare event system
@@ -311,12 +303,22 @@ export function dispatchEvent(
303 );
304 }
305 } else {
314 - dispatchEventForLegacyPluginEventSystem(
315 - topLevelType,
316 - eventSystemFlags,
317 - nativeEvent,
318 - null,
319 - );
306 + if (enableModernEventSystem) {
307 + dispatchEventForPluginEventSystem(
308 + topLevelType,
309 + eventSystemFlags,
310 + nativeEvent,
311 + null,
312 + container,
313 + );
314 + } else {
315 + dispatchEventForLegacyPluginEventSystem(
316 + topLevelType,
317 + eventSystemFlags,
318 + nativeEvent,
319 + null,
320 + );
321 + }
322 }
323 }
324
@@ -324,7 +326,7 @@ export function dispatchEvent(
326 export function attemptToDispatchEvent(
327 topLevelType: DOMTopLevelEventType,
328 eventSystemFlags: EventSystemFlags,
327 - container: Document | Element | Node,
329 + container: Document | Element,
330 nativeEvent: AnyNativeEvent,
331 ): null | Container | SuspenseInstance {
332 // TODO: Warn if _enabled is false.
@@ -372,12 +374,22 @@ export function attemptToDispatchEvent(
374
375 if (enableDeprecatedFlareAPI) {
376 if (eventSystemFlags & PLUGIN_EVENT_SYSTEM) {
375 - dispatchEventForLegacyPluginEventSystem(
376 - topLevelType,
377 - eventSystemFlags,
378 - nativeEvent,
379 - targetInst,
380 - );
377 + if (enableModernEventSystem) {
378 + dispatchEventForPluginEventSystem(
379 + topLevelType,
380 + eventSystemFlags,
381 + nativeEvent,
382 + targetInst,
383 + container,
384 + );
385 + } else {
386 + dispatchEventForLegacyPluginEventSystem(
387 + topLevelType,
388 + eventSystemFlags,
389 + nativeEvent,
390 + targetInst,
391 + );
392 + }
393 }
394 if (eventSystemFlags & RESPONDER_EVENT_SYSTEM) {
395 // React Flare event system
@@ -390,12 +402,22 @@ export function attemptToDispatchEvent(
402 );
403 }
404 } else {
393 - dispatchEventForLegacyPluginEventSystem(
394 - topLevelType,
395 - eventSystemFlags,
396 - nativeEvent,
397 - targetInst,
398 - );
405 + if (enableModernEventSystem) {
406 + dispatchEventForPluginEventSystem(
407 + topLevelType,
408 + eventSystemFlags,
409 + nativeEvent,
410 + targetInst,
411 + container,
412 + );
413 + } else {
414 + dispatchEventForLegacyPluginEventSystem(
415 + topLevelType,
416 + eventSystemFlags,
417 + nativeEvent,
418 + targetInst,
419 + );
420 + }
421 }
422 // We're not blocked on anything.
423 return null;
packages/react-dom/src/events/ReactDOMEventReplaying.js
+36 -6
@@ -16,6 +16,7 @@ import type {FiberRoot} from 'react-reconciler/src/ReactFiberRoot';
16 import {
17 enableDeprecatedFlareAPI,
18 enableSelectiveHydration,
19 + enableModernEventSystem,
20 } from 'shared/ReactFeatureFlags';
21 import {
22 unstable_runWithPriority as runWithPriority,
@@ -118,13 +119,14 @@ import {
119 } from './DOMTopLevelEventTypes';
120 import {IS_REPLAYED} from 'legacy-events/EventSystemFlags';
121 import {legacyListenToTopLevelEvent} from './DOMLegacyEventPluginSystem';
122 +import {listenToTopLevelEvent} from './DOMModernPluginEventSystem';
123
124 type QueuedReplayableEvent = {|
125 blockedOn: null | Container | SuspenseInstance,
126 topLevelType: DOMTopLevelEventType,
127 eventSystemFlags: EventSystemFlags,
128 nativeEvent: AnyNativeEvent,
127 - container: Document | Element | Node,
129 + container: Document | Element,
130 |};
131
132 let hasScheduledReplayAttempt = false;
@@ -211,12 +213,22 @@ export function isReplayableDiscreteEvent(
213 return discreteReplayableEvents.indexOf(eventType) > -1;
214 }
215
216 +function trapReplayableEventForContainer(
217 + topLevelType: DOMTopLevelEventType,
218 + container: Container,
219 + listenerMap: Map<DOMTopLevelEventType | string, null | (any => void)>,
220 +) {
221 + listenToTopLevelEvent(topLevelType, ((container: any): Element), listenerMap);
222 +}
223 +
224 function trapReplayableEventForDocument(
225 topLevelType: DOMTopLevelEventType,
226 document: Document,
227 listenerMap: Map<DOMTopLevelEventType | string, null | (any => void)>,
228 ) {
219 - legacyListenToTopLevelEvent(topLevelType, document, listenerMap);
229 + if (!enableModernEventSystem) {
230 + legacyListenToTopLevelEvent(topLevelType, document, listenerMap);
231 + }
232 if (enableDeprecatedFlareAPI) {
233 // Trap events for the responder system.
234 const topLevelTypeString = unsafeCastDOMTopLevelTypeToString(topLevelType);
@@ -241,12 +253,30 @@ export function eagerlyTrapReplayableEvents(
253 document: Document,
254 ) {
255 const listenerMapForDoc = getListenerMapForElement(document);
256 + let listenerMapForContainer;
257 + if (enableModernEventSystem) {
258 + listenerMapForContainer = getListenerMapForElement(container);
259 + }
260 // Discrete
261 discreteReplayableEvents.forEach(topLevelType => {
262 + if (enableModernEventSystem) {
263 + trapReplayableEventForContainer(
264 + topLevelType,
265 + container,
266 + listenerMapForContainer,
267 + );
268 + }
269 trapReplayableEventForDocument(topLevelType, document, listenerMapForDoc);
270 });
271 // Continuous
272 continuousReplayableEvents.forEach(topLevelType => {
273 + if (enableModernEventSystem) {
274 + trapReplayableEventForContainer(
275 + topLevelType,
276 + container,
277 + listenerMapForContainer,
278 + );
279 + }
280 trapReplayableEventForDocument(topLevelType, document, listenerMapForDoc);
281 });
282 }
@@ -255,7 +285,7 @@ function createQueuedReplayableEvent(
285 blockedOn: null | Container | SuspenseInstance,
286 topLevelType: DOMTopLevelEventType,
287 eventSystemFlags: EventSystemFlags,
258 - container: Document | Element | Node,
288 + container: Document | Element,
289 nativeEvent: AnyNativeEvent,
290 ): QueuedReplayableEvent {
291 return {
@@ -271,7 +301,7 @@ export function queueDiscreteEvent(
301 blockedOn: null | Container | SuspenseInstance,
302 topLevelType: DOMTopLevelEventType,
303 eventSystemFlags: EventSystemFlags,
274 - container: Document | Element | Node,
304 + container: Document | Element,
305 nativeEvent: AnyNativeEvent,
306 ): void {
307 const queuedEvent = createQueuedReplayableEvent(
@@ -346,7 +376,7 @@ function accumulateOrCreateContinuousQueuedReplayableEvent(
376 blockedOn: null | Container | SuspenseInstance,
377 topLevelType: DOMTopLevelEventType,
378 eventSystemFlags: EventSystemFlags,
349 - container: Document | Element | Node,
379 + container: Document | Element,
380 nativeEvent: AnyNativeEvent,
381 ): QueuedReplayableEvent {
382 if (
@@ -381,7 +411,7 @@ export function queueIfContinuousEvent(
411 blockedOn: null | Container | SuspenseInstance,
412 topLevelType: DOMTopLevelEventType,
413 eventSystemFlags: EventSystemFlags,
384 - container: Document | Element | Node,
414 + container: Document | Element,
415 nativeEvent: AnyNativeEvent,
416 ): boolean {
417 // These set relatedTarget to null because the replayed event will be treated as if we
packages/react-dom/src/events/SelectEventPlugin.js
+1 -1
@@ -26,7 +26,7 @@ import getActiveElement from '../client/getActiveElement';
26 import {getNodeFromInstance} from '../client/ReactDOMComponentTree';
27 import {hasSelectionCapabilities} from '../client/ReactInputSelection';
28 import {DOCUMENT_NODE} from '../shared/HTMLNodeType';
29 -import {isListeningToAllDependencies} from './DOMLegacyEventPluginSystem';
29 +import {isListeningToAllDependencies} from './DOMEventListenerMap';
30
31 const skipSelectionChangeEvent =
32 canUseDOM && 'documentMode' in document && document.documentMode <= 11;
packages/react-dom/src/events/forks/EventListener-www.js
+6 -6
@@ -16,16 +16,16 @@ export function addEventBubbleListener(
16 element: Element,
17 eventType: string,
18 listener: Function,
19 -): void {
20 - EventListenerWWW.listen(element, eventType, listener);
19 +) {
20 + return EventListenerWWW.listen(element, eventType, listener);
21 }
22
23 export function addEventCaptureListener(
24 element: Element,
25 eventType: string,
26 listener: Function,
27 -): void {
28 - EventListenerWWW.capture(element, eventType, listener);
27 +) {
28 + return EventListenerWWW.capture(element, eventType, listener);
29 }
30
31 export function addEventCaptureListenerWithPassiveFlag(
@@ -33,8 +33,8 @@ export function addEventCaptureListenerWithPassiveFlag(
33 eventType: string,
34 listener: Function,
35 passive: boolean,
36 -): void {
37 - EventListenerWWW.captureWithPassiveFlag(
36 +) {
37 + return EventListenerWWW.captureWithPassiveFlag(
38 element,
39 eventType,
40 listener,
scripts/error-codes/codes.json
+2 -1
@@ -345,5 +345,6 @@
345 "344": "Expected prepareToHydrateHostSuspenseInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.",
346 "345": "Root did not complete. This is a bug in React.",
347 "346": "An event responder context was used outside of an event cycle.",
348 - "347": "Maps are not valid as a React child (found: %s). Consider converting children to an array of keyed ReactElements instead."
348 + "347": "Maps are not valid as a React child (found: %s). Consider converting children to an array of keyed ReactElements instead.",
349 + "348": "ensureListeningTo(): received a container that was not an element node. This is likely a bug in React."
350 }