@samitouri / QOS-React / commits / 993ca533b4

Enable eager listeners statically (#19983)

Dan Abramov committed Oct 8, 2020 at 19:32 UTC 993ca533b42756811731f6b7791ae06a35ee6b4d
21 files changed +55 -402
packages/react-dom/src/__tests__/ReactDOMFiber-test.js
-1
@@ -1040,7 +1040,6 @@ describe('ReactDOMFiber', () => {
1040 expect(ops).toEqual([]);
1041 });
1042
1043 - // @gate enableEagerRootListeners
1043 it('listens to events that do not exist in the Portal subtree', () => {
1044 const onClick = jest.fn();
1045
packages/react-dom/src/client/ReactDOMComponent.js
+5 -83
@@ -61,12 +61,7 @@ import {
61 shouldRemoveAttribute,
62 } from '../shared/DOMProperty';
63 import assertValidProps from '../shared/assertValidProps';
64 -import {
65 - DOCUMENT_NODE,
66 - ELEMENT_NODE,
67 - COMMENT_NODE,
68 - DOCUMENT_FRAGMENT_NODE,
69 -} from '../shared/HTMLNodeType';
64 +import {DOCUMENT_NODE} from '../shared/HTMLNodeType';
65 import isCustomComponent from '../shared/isCustomComponent';
66 import possibleStandardNames from '../shared/possibleStandardNames';
67 import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook';
@@ -74,12 +69,8 @@ import {validateProperties as validateInputProperties} from '../shared/ReactDOMN
69 import {validateProperties as validateUnknownProperties} from '../shared/ReactDOMUnknownPropertyHook';
70 import {REACT_OPAQUE_ID_TYPE} from 'shared/ReactSymbols';
71
72 +import {enableTrustedTypesIntegration} from 'shared/ReactFeatureFlags';
73 import {
78 - enableTrustedTypesIntegration,
79 - enableEagerRootListeners,
80 -} from 'shared/ReactFeatureFlags';
81 -import {
82 - listenToReactEvent,
74 mediaEventTypes,
75 listenToNonDelegatedEvent,
76 } from '../events/DOMPluginEventSystem';
@@ -253,39 +244,6 @@ if (__DEV__) {
244 };
245 }
246
256 -export function ensureListeningTo(
257 - rootContainerInstance: Element | Node,
258 - reactPropEvent: string,
259 - targetElement: Element | null,
260 -): void {
261 - if (!enableEagerRootListeners) {
262 - // If we have a comment node, then use the parent node,
263 - // which should be an element.
264 - const rootContainerElement =
265 - rootContainerInstance.nodeType === COMMENT_NODE
266 - ? rootContainerInstance.parentNode
267 - : rootContainerInstance;
268 - if (__DEV__) {
269 - if (
270 - rootContainerElement == null ||
271 - (rootContainerElement.nodeType !== ELEMENT_NODE &&
272 - // This is to support rendering into a ShadowRoot:
273 - rootContainerElement.nodeType !== DOCUMENT_FRAGMENT_NODE)
274 - ) {
275 - console.error(
276 - 'ensureListeningTo(): received a container that was not an element node. ' +
277 - 'This is likely a bug in React. Please file an issue.',
278 - );
279 - }
280 - }
281 - listenToReactEvent(
282 - reactPropEvent,
283 - ((rootContainerElement: any): Element),
284 - targetElement,
285 - );
286 - }
287 -}
288 -
247 function getOwnerDocumentFromRootContainer(
248 rootContainerElement: Element | Document,
249 ): Document {
@@ -364,9 +322,7 @@ function setInitialDOMProperties(
322 if (__DEV__ && typeof nextProp !== 'function') {
323 warnForInvalidEventListener(propKey, nextProp);
324 }
367 - if (!enableEagerRootListeners) {
368 - ensureListeningTo(rootContainerElement, propKey, domElement);
369 - } else if (propKey === 'onScroll') {
325 + if (propKey === 'onScroll') {
326 listenToNonDelegatedEvent('scroll', domElement);
327 }
328 }
@@ -577,11 +533,6 @@ export function setInitialProperties(
533 // We listen to this event in case to ensure emulated bubble
534 // listeners still fire for the invalid event.
535 listenToNonDelegatedEvent('invalid', domElement);
580 - if (!enableEagerRootListeners) {
581 - // For controlled components we always need to ensure we're listening
582 - // to onChange. Even if there is no listener.
583 - ensureListeningTo(rootContainerElement, 'onChange', domElement);
584 - }
536 break;
537 case 'option':
538 ReactDOMOptionValidateProps(domElement, rawProps);
@@ -593,11 +544,6 @@ export function setInitialProperties(
544 // We listen to this event in case to ensure emulated bubble
545 // listeners still fire for the invalid event.
546 listenToNonDelegatedEvent('invalid', domElement);
596 - if (!enableEagerRootListeners) {
597 - // For controlled components we always need to ensure we're listening
598 - // to onChange. Even if there is no listener.
599 - ensureListeningTo(rootContainerElement, 'onChange', domElement);
600 - }
547 break;
548 case 'textarea':
549 ReactDOMTextareaInitWrapperState(domElement, rawProps);
@@ -605,11 +551,6 @@ export function setInitialProperties(
551 // We listen to this event in case to ensure emulated bubble
552 // listeners still fire for the invalid event.
553 listenToNonDelegatedEvent('invalid', domElement);
608 - if (!enableEagerRootListeners) {
609 - // For controlled components we always need to ensure we're listening
610 - // to onChange. Even if there is no listener.
611 - ensureListeningTo(rootContainerElement, 'onChange', domElement);
612 - }
554 break;
555 default:
556 props = rawProps;
@@ -827,9 +768,7 @@ export function diffProperties(
768 if (__DEV__ && typeof nextProp !== 'function') {
769 warnForInvalidEventListener(propKey, nextProp);
770 }
830 - if (!enableEagerRootListeners) {
831 - ensureListeningTo(rootContainerElement, propKey, domElement);
832 - } else if (propKey === 'onScroll') {
771 + if (propKey === 'onScroll') {
772 listenToNonDelegatedEvent('scroll', domElement);
773 }
774 }
@@ -983,11 +922,6 @@ export function diffHydratedProperties(
922 // We listen to this event in case to ensure emulated bubble
923 // listeners still fire for the invalid event.
924 listenToNonDelegatedEvent('invalid', domElement);
986 - if (!enableEagerRootListeners) {
987 - // For controlled components we always need to ensure we're listening
988 - // to onChange. Even if there is no listener.
989 - ensureListeningTo(rootContainerElement, 'onChange', domElement);
990 - }
925 break;
926 case 'option':
927 ReactDOMOptionValidateProps(domElement, rawProps);
@@ -997,22 +931,12 @@ export function diffHydratedProperties(
931 // We listen to this event in case to ensure emulated bubble
932 // listeners still fire for the invalid event.
933 listenToNonDelegatedEvent('invalid', domElement);
1000 - if (!enableEagerRootListeners) {
1001 - // For controlled components we always need to ensure we're listening
1002 - // to onChange. Even if there is no listener.
1003 - ensureListeningTo(rootContainerElement, 'onChange', domElement);
1004 - }
934 break;
935 case 'textarea':
936 ReactDOMTextareaInitWrapperState(domElement, rawProps);
937 // We listen to this event in case to ensure emulated bubble
938 // listeners still fire for the invalid event.
939 listenToNonDelegatedEvent('invalid', domElement);
1011 - if (!enableEagerRootListeners) {
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', domElement);
1015 - }
940 break;
941 }
942
@@ -1079,9 +1003,7 @@ export function diffHydratedProperties(
1003 if (__DEV__ && typeof nextProp !== 'function') {
1004 warnForInvalidEventListener(propKey, nextProp);
1005 }
1082 - if (!enableEagerRootListeners) {
1083 - ensureListeningTo(rootContainerElement, propKey, domElement);
1084 - } else if (propKey === 'onScroll') {
1006 + if (propKey === 'onScroll') {
1007 listenToNonDelegatedEvent('scroll', domElement);
1008 }
1009 }
packages/react-dom/src/client/ReactDOMEventHandle.js
+3 -84
@@ -16,23 +16,19 @@ import type {
16
17 import {allNativeEvents} from '../events/EventRegistry';
18 import {
19 - getClosestInstanceFromNode,
19 getEventHandlerListeners,
20 setEventHandlerListeners,
22 - getFiberFromScopeInstance,
21 doesTargetHaveEventHandle,
22 addEventHandleToTarget,
23 } from './ReactDOMComponentTree';
26 -import {ELEMENT_NODE, COMMENT_NODE} from '../shared/HTMLNodeType';
24 +import {ELEMENT_NODE} from '../shared/HTMLNodeType';
25 import {listenToNativeEvent} from '../events/DOMPluginEventSystem';
26
29 -import {HostRoot, HostPortal} from 'react-reconciler/src/ReactWorkTags';
27 import {IS_EVENT_HANDLE_NON_MANAGED_NODE} from '../events/EventSystemFlags';
28
29 import {
30 enableScopeAPI,
31 enableCreateEventHandleAPI,
35 - enableEagerRootListeners,
32 } from 'shared/ReactFeatureFlags';
33 import invariant from 'shared/invariant';
34
@@ -40,19 +36,6 @@ type EventHandleOptions = {|
36 capture?: boolean,
37 |};
38
43 -function getNearestRootOrPortalContainer(node: Fiber): null | Element {
44 - while (node !== null) {
45 - const tag = node.tag;
46 - // Once we encounter a host container or root container
47 - // we can return their DOM instance.
48 - if (tag === HostRoot || tag === HostPortal) {
49 - return node.stateNode.containerInfo;
50 - }
51 - node = node.return;
52 - }
53 - return null;
54 -}
55 -
39 function isValidEventTarget(target: EventTarget | ReactScopeInstance): boolean {
40 return typeof (target: Object).addEventListener === 'function';
41 }
@@ -73,79 +56,15 @@ function createEventHandleListener(
56 };
57 }
58
76 -function registerEventOnNearestTargetContainer(
77 - targetFiber: Fiber,
78 - domEventName: DOMEventName,
79 - isCapturePhaseListener: boolean,
80 - targetElement: Element | null,
81 -): void {
82 - if (!enableEagerRootListeners) {
83 - // If it is, find the nearest root or portal and make it
84 - // our event handle target container.
85 - let targetContainer = getNearestRootOrPortalContainer(targetFiber);
86 - if (targetContainer === null) {
87 - if (__DEV__) {
88 - console.error(
89 - 'ReactDOM.createEventHandle: setListener called on an target ' +
90 - 'that did not have a corresponding root. This is likely a bug in React.',
91 - );
92 - }
93 - return;
94 - }
95 - if (targetContainer.nodeType === COMMENT_NODE) {
96 - targetContainer = ((targetContainer.parentNode: any): Element);
97 - }
98 - listenToNativeEvent(
99 - domEventName,
100 - isCapturePhaseListener,
101 - targetContainer,
102 - targetElement,
103 - );
104 - }
105 -}
106 -
59 function registerReactDOMEvent(
60 target: EventTarget | ReactScopeInstance,
61 domEventName: DOMEventName,
62 isCapturePhaseListener: boolean,
63 ): void {
112 - // Check if the target is a DOM element.
64 if ((target: any).nodeType === ELEMENT_NODE) {
114 - if (!enableEagerRootListeners) {
115 - const targetElement = ((target: any): Element);
116 - // Check if the DOM element is managed by React.
117 - const targetFiber = getClosestInstanceFromNode(targetElement);
118 - if (targetFiber === null) {
119 - if (__DEV__) {
120 - console.error(
121 - 'ReactDOM.createEventHandle: setListener called on an element ' +
122 - 'target that is not managed by React. Ensure React rendered the DOM element.',
123 - );
124 - }
125 - return;
126 - }
127 - registerEventOnNearestTargetContainer(
128 - targetFiber,
129 - domEventName,
130 - isCapturePhaseListener,
131 - targetElement,
132 - );
133 - }
65 + // Do nothing. We already attached all root listeners.
66 } else if (enableScopeAPI && isReactScope(target)) {
135 - if (!enableEagerRootListeners) {
136 - const scopeTarget = ((target: any): ReactScopeInstance);
137 - const targetFiber = getFiberFromScopeInstance(scopeTarget);
138 - if (targetFiber === null) {
139 - // Scope is unmounted, do not proceed.
140 - return;
141 - }
142 - registerEventOnNearestTargetContainer(
143 - targetFiber,
144 - domEventName,
145 - isCapturePhaseListener,
146 - null,
147 - );
148 - }
67 + // Do nothing. We already attached all root listeners.
68 } else if (isValidEventTarget(target)) {
69 const eventTarget = ((target: any): EventTarget);
70 // These are valid event targets, but they are also
packages/react-dom/src/client/ReactDOMHostConfig.js
+2 -10
@@ -67,13 +67,9 @@ import {
67 enableFundamentalAPI,
68 enableCreateEventHandleAPI,
69 enableScopeAPI,
70 - enableEagerRootListeners,
70 } from 'shared/ReactFeatureFlags';
71 import {HostComponent, HostText} from 'react-reconciler/src/ReactWorkTags';
73 -import {
74 - listenToReactEvent,
75 - listenToAllSupportedEvents,
76 -} from '../events/DOMPluginEventSystem';
72 +import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem';
73
74 export type Type = string;
75 export type Props = {
@@ -1073,11 +1069,7 @@ export function makeOpaqueHydratingObject(
1069 }
1070
1071 export function preparePortalMount(portalInstance: Instance): void {
1076 - if (enableEagerRootListeners) {
1077 - listenToAllSupportedEvents(portalInstance);
1078 - } else {
1079 - listenToReactEvent('onMouseEnter', portalInstance, null);
1080 - }
1072 + listenToAllSupportedEvents(portalInstance);
1073 }
1074
1075 export function prepareScopeUpdate(
packages/react-dom/src/client/ReactDOMRoot.js
+4 -27
@@ -36,14 +36,12 @@ import {
36 unmarkContainerAsRoot,
37 } from './ReactDOMComponentTree';
38 import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem';
39 -import {eagerlyTrapReplayableEvents} from '../events/ReactDOMEventReplaying';
39 import {
40 ELEMENT_NODE,
41 COMMENT_NODE,
42 DOCUMENT_NODE,
43 DOCUMENT_FRAGMENT_NODE,
44 } from '../shared/HTMLNodeType';
46 -import {ensureListeningTo} from './ReactDOMComponent';
45
46 import {
47 createContainer,
@@ -52,7 +50,6 @@ import {
50 registerMutableSourceForHydration,
51 } from 'react-reconciler/src/ReactFiberReconciler';
52 import invariant from 'shared/invariant';
55 -import {enableEagerRootListeners} from 'shared/ReactFeatureFlags';
53 import {
54 BlockingRoot,
55 ConcurrentRoot,
@@ -133,30 +130,10 @@ function createRootImpl(
130 null;
131 const root = createContainer(container, tag, hydrate, hydrationCallbacks);
132 markContainerAsRoot(root.current, container);
136 - const containerNodeType = container.nodeType;
137 -
138 - if (enableEagerRootListeners) {
139 - const rootContainerElement =
140 - container.nodeType === COMMENT_NODE ? container.parentNode : container;
141 - listenToAllSupportedEvents(rootContainerElement);
142 - } else {
143 - if (hydrate && tag !== LegacyRoot) {
144 - const doc =
145 - containerNodeType === DOCUMENT_NODE
146 - ? container
147 - : container.ownerDocument;
148 - // We need to cast this because Flow doesn't work
149 - // with the hoisted containerNodeType. If we inline
150 - // it, then Flow doesn't complain. We intentionally
151 - // hoist it to reduce code-size.
152 - eagerlyTrapReplayableEvents(container, ((doc: any): Document));
153 - } else if (
154 - containerNodeType !== DOCUMENT_FRAGMENT_NODE &&
155 - containerNodeType !== DOCUMENT_NODE
156 - ) {
157 - ensureListeningTo(container, 'onMouseEnter', null);
158 - }
159 - }
133 +
134 + const rootContainerElement =
135 + container.nodeType === COMMENT_NODE ? container.parentNode : container;
136 + listenToAllSupportedEvents(rootContainerElement);
137
138 if (mutableSources) {
139 for (let i = 0; i < mutableSources.length; i++) {
packages/react-dom/src/events/DOMPluginEventSystem.js
+20 -77
@@ -21,7 +21,7 @@ import type {
21 } from './ReactSyntheticEventType';
22 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
23
24 -import {registrationNameDependencies, allNativeEvents} from './EventRegistry';
24 +import {allNativeEvents} from './EventRegistry';
25 import {
26 IS_CAPTURE_PHASE,
27 IS_EVENT_HANDLE_NON_MANAGED_NODE,
@@ -51,7 +51,6 @@ import {
51 enableLegacyFBSupport,
52 enableCreateEventHandleAPI,
53 enableScopeAPI,
54 - enableEagerRootListeners,
54 } from 'shared/ReactFeatureFlags';
55 import {
56 invokeGuardedCallbackAndCatchFirstError,
@@ -320,32 +319,30 @@ const listeningMarker =
319 .slice(2);
320
321 export function listenToAllSupportedEvents(rootContainerElement: EventTarget) {
323 - if (enableEagerRootListeners) {
324 - if ((rootContainerElement: any)[listeningMarker]) {
325 - // Performance optimization: don't iterate through events
326 - // for the same portal container or root node more than once.
327 - // TODO: once we remove the flag, we may be able to also
328 - // remove some of the bookkeeping maps used for laziness.
329 - return;
330 - }
331 - (rootContainerElement: any)[listeningMarker] = true;
332 - allNativeEvents.forEach(domEventName => {
333 - if (!nonDelegatedEvents.has(domEventName)) {
334 - listenToNativeEvent(
335 - domEventName,
336 - false,
337 - ((rootContainerElement: any): Element),
338 - null,
339 - );
340 - }
322 + if ((rootContainerElement: any)[listeningMarker]) {
323 + // Performance optimization: don't iterate through events
324 + // for the same portal container or root node more than once.
325 + // TODO: once we remove the flag, we may be able to also
326 + // remove some of the bookkeeping maps used for laziness.
327 + return;
328 + }
329 + (rootContainerElement: any)[listeningMarker] = true;
330 + allNativeEvents.forEach(domEventName => {
331 + if (!nonDelegatedEvents.has(domEventName)) {
332 listenToNativeEvent(
333 domEventName,
343 - true,
334 + false,
335 ((rootContainerElement: any): Element),
336 null,
337 );
347 - });
348 - }
338 + }
339 + listenToNativeEvent(
340 + domEventName,
341 + true,
342 + ((rootContainerElement: any): Element),
343 + null,
344 + );
345 + });
346 }
347
348 export function listenToNativeEvent(
@@ -411,60 +408,6 @@ export function listenToNativeEvent(
408 }
409 }
410
414 -export function listenToReactEvent(
415 - reactEvent: string,
416 - rootContainerElement: Element,
417 - targetElement: Element | null,
418 -): void {
419 - if (!enableEagerRootListeners) {
420 - const dependencies = registrationNameDependencies[reactEvent];
421 - const dependenciesLength = dependencies.length;
422 - // If the dependencies length is 1, that means we're not using a polyfill
423 - // plugin like ChangeEventPlugin, BeforeInputPlugin, EnterLeavePlugin
424 - // and SelectEventPlugin. We always use the native bubble event phase for
425 - // these plugins and emulate two phase event dispatching. SimpleEventPlugin
426 - // always only has a single dependency and SimpleEventPlugin events also
427 - // use either the native capture event phase or bubble event phase, there
428 - // is no emulation (except for focus/blur, but that will be removed soon).
429 - const isPolyfillEventPlugin = dependenciesLength !== 1;
430 -
431 - if (isPolyfillEventPlugin) {
432 - const listenerSet = getEventListenerSet(rootContainerElement);
433 - // When eager listeners are off, this Set has a dual purpose: it both
434 - // captures which native listeners we registered (e.g. "click__bubble")
435 - // and *React* lazy listeners (e.g. "onClick") so we don't do extra checks.
436 - // This second usage does not exist in the eager mode.
437 - if (!listenerSet.has(reactEvent)) {
438 - listenerSet.add(reactEvent);
439 - for (let i = 0; i < dependenciesLength; i++) {
440 - listenToNativeEvent(
441 - dependencies[i],
442 - false,
443 - rootContainerElement,
444 - targetElement,
445 - );
446 - }
447 - }
448 - } else {
449 - const isCapturePhaseListener =
450 - reactEvent.substr(-7) === 'Capture' &&
451 - // Edge case: onGotPointerCapture and onLostPointerCapture
452 - // end with "Capture" but that's part of their event names.
453 - // The Capture versions would end with CaptureCapture.
454 - // So we have to check against that.
455 - // This check works because none of the events we support
456 - // end with "Pointer".
457 - reactEvent.substr(-14, 7) !== 'Pointer';
458 - listenToNativeEvent(
459 - dependencies[0],
460 - isCapturePhaseListener,
461 - rootContainerElement,
462 - targetElement,
463 - );
464 - }
465 - }
466 -}
467 -
411 function addTrappedEventListener(
412 targetContainer: EventTarget,
413 domEventName: DOMEventName,
packages/react-dom/src/events/ReactDOMEventListener.js
+8 -11
@@ -40,7 +40,6 @@ import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
40
41 import {
42 enableLegacyFBSupport,
43 - enableEagerRootListeners,
43 decoupleUpdatePriorityFromScheduler,
44 } from 'shared/ReactFeatureFlags';
45 import {
@@ -188,16 +187,14 @@ export function dispatchEvent(
187 if (!_enabled) {
188 return;
189 }
191 - let allowReplay = true;
192 - if (enableEagerRootListeners) {
193 - // TODO: replaying capture phase events is currently broken
194 - // because we used to do it during top-level native bubble handlers
195 - // but now we use different bubble and capture handlers.
196 - // In eager mode, we attach capture listeners early, so we need
197 - // to filter them out until we fix the logic to handle them correctly.
198 - // This could've been outside the flag but I put it inside to reduce risk.
199 - allowReplay = (eventSystemFlags & IS_CAPTURE_PHASE) === 0;
200 - }
190 +
191 + // TODO: replaying capture phase events is currently broken
192 + // because we used to do it during top-level native bubble handlers
193 + // but now we use different bubble and capture handlers.
194 + // In eager mode, we attach capture listeners early, so we need
195 + // to filter them out until we fix the logic to handle them correctly.
196 + const allowReplay = (eventSystemFlags & IS_CAPTURE_PHASE) === 0;
197 +
198 if (
199 allowReplay &&
200 hasQueuedDiscreteEvents() &&
packages/react-dom/src/events/ReactDOMEventReplaying.js
+1 -45
@@ -14,10 +14,7 @@ import type {EventSystemFlags} from './EventSystemFlags';
14 import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
15 import type {LanePriority} from 'react-reconciler/src/ReactFiberLane';
16
17 -import {
18 - enableSelectiveHydration,
19 - enableEagerRootListeners,
20 -} from 'shared/ReactFeatureFlags';
17 +import {enableSelectiveHydration} from 'shared/ReactFeatureFlags';
18 import {
19 unstable_runWithPriority as runWithPriority,
20 unstable_scheduleCallback as scheduleCallback,
@@ -85,7 +82,6 @@ type PointerEvent = Event & {
82 };
83
84 import {IS_REPLAYED} from './EventSystemFlags';
88 -import {listenToNativeEvent} from './DOMPluginEventSystem';
85
86 type QueuedReplayableEvent = {|
87 blockedOn: null | Container | SuspenseInstance,
@@ -159,50 +155,10 @@ const discreteReplayableEvents: Array<DOMEventName> = [
155 'submit',
156 ];
157
162 -const continuousReplayableEvents: Array<DOMEventName> = [
163 - 'dragenter',
164 - 'dragleave',
165 - 'focusin',
166 - 'focusout',
167 - 'mouseover',
168 - 'mouseout',
169 - 'pointerover',
170 - 'pointerout',
171 - 'gotpointercapture',
172 - 'lostpointercapture',
173 -];
174 -
158 export function isReplayableDiscreteEvent(eventType: DOMEventName): boolean {
159 return discreteReplayableEvents.indexOf(eventType) > -1;
160 }
161
179 -function trapReplayableEventForContainer(
180 - domEventName: DOMEventName,
181 - container: Container,
182 -) {
183 - // When the flag is on, we do this in a unified codepath elsewhere.
184 - if (!enableEagerRootListeners) {
185 - listenToNativeEvent(domEventName, false, ((container: any): Element), null);
186 - }
187 -}
188 -
189 -export function eagerlyTrapReplayableEvents(
190 - container: Container,
191 - document: Document,
192 -) {
193 - // When the flag is on, we do this in a unified codepath elsewhere.
194 - if (!enableEagerRootListeners) {
195 - // Discrete
196 - discreteReplayableEvents.forEach(domEventName => {
197 - trapReplayableEventForContainer(domEventName, container);
198 - });
199 - // Continuous
200 - continuousReplayableEvents.forEach(domEventName => {
201 - trapReplayableEventForContainer(domEventName, container);
202 - });
203 - }
204 -}
205 -
162 function createQueuedReplayableEvent(
163 blockedOn: null | Container | SuspenseInstance,
164 domEventName: DOMEventName,
packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js
+1 -1
@@ -2643,7 +2643,7 @@ describe('DOMPluginEventSystem', () => {
2643 expect(log[5]).toEqual(['bubble', buttonElement]);
2644 });
2645
2646 - // @gate experimental && enableEagerRootListeners
2646 + // @gate experimental
2647 it('propagates known createEventHandle events through portals without inner listeners', () => {
2648 const buttonRef = React.createRef();
2649 const divRef = React.createRef();
packages/react-dom/src/events/plugins/SelectEventPlugin.js
+1 -22
@@ -16,14 +16,10 @@ import {canUseDOM} from 'shared/ExecutionEnvironment';
16 import {SyntheticEvent} from '../../events/SyntheticEvent';
17 import isTextInputElement from '../isTextInputElement';
18 import shallowEqual from 'shared/shallowEqual';
19 -import {enableEagerRootListeners} from 'shared/ReactFeatureFlags';
19
20 import {registerTwoPhaseEvent} from '../EventRegistry';
21 import getActiveElement from '../../client/getActiveElement';
23 -import {
24 - getNodeFromInstance,
25 - getEventListenerSet,
26 -} from '../../client/ReactDOMComponentTree';
22 +import {getNodeFromInstance} from '../../client/ReactDOMComponentTree';
23 import {hasSelectionCapabilities} from '../../client/ReactInputSelection';
24 import {DOCUMENT_NODE} from '../../shared/HTMLNodeType';
25 import {accumulateTwoPhaseListeners} from '../DOMPluginEventSystem';
@@ -154,23 +150,6 @@ function extractEvents(
150 eventSystemFlags: EventSystemFlags,
151 targetContainer: EventTarget,
152 ) {
157 - if (!enableEagerRootListeners) {
158 - const eventListenerSet = getEventListenerSet(targetContainer);
159 - // Track whether all listeners exists for this plugin. If none exist, we do
160 - // not extract events. See #3639.
161 - if (
162 - // If we are handling selectionchange, then we don't need to
163 - // check for the other dependencies, as selectionchange is only
164 - // event attached from the onChange plugin and we don't expose an
165 - // onSelectionChange event from React.
166 - domEventName !== 'selectionchange' &&
167 - !eventListenerSet.has('onSelect') &&
168 - !eventListenerSet.has('onSelectCapture')
169 - ) {
170 - return;
171 - }
172 - }
173 -
153 const targetNode = targetInst ? getNodeFromInstance(targetInst) : window;
154
155 switch (domEventName) {
packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js
+10 -30
@@ -516,36 +516,16 @@ describe('SimpleEventPlugin', function() {
516 return nativeAddEventListener.apply(this, arguments);
517 };
518
519 - ReactDOM.render(
520 - <div
521 - // Affected by the intervention:
522 - // https://github.com/facebook/react/issues/19651
523 - onTouchStart={() => {}}
524 - onTouchMove={() => {}}
525 - onWheel={() => {}}
526 - // A few events that should be unaffected:
527 - onClick={() => {}}
528 - onScroll={() => {}}
529 - onTouchEnd={() => {}}
530 - onChange={() => {}}
531 - onPointerDown={() => {}}
532 - onPointerMove={() => {}}
533 - />,
534 - container,
535 - );
536 -
537 - if (gate(flags => flags.enableEagerRootListeners)) {
538 - expect(passiveEvents).toEqual([
539 - 'touchstart',
540 - 'touchstart',
541 - 'touchmove',
542 - 'touchmove',
543 - 'wheel',
544 - 'wheel',
545 - ]);
546 - } else {
547 - expect(passiveEvents).toEqual(['touchstart', 'touchmove', 'wheel']);
548 - }
519 + ReactDOM.render(<div />, container);
520 +
521 + expect(passiveEvents).toEqual([
522 + 'touchstart',
523 + 'touchstart',
524 + 'touchmove',
525 + 'touchmove',
526 + 'wheel',
527 + 'wheel',
528 + ]);
529 });
530 });
531 });
packages/shared/ReactFeatureFlags.js
-2
@@ -133,6 +133,4 @@ export const decoupleUpdatePriorityFromScheduler = false;
133
134 export const enableDiscreteEventFlushingChange = false;
135
136 -export const enableEagerRootListeners = true;
137 -
136 export const enableDoubleInvokingEffects = false;
packages/shared/forks/ReactFeatureFlags.native-fb.js
-1
@@ -49,7 +49,6 @@ export const enableNewReconciler = false;
49 export const deferRenderPhaseUpdateToNextBatch = true;
50 export const decoupleUpdatePriorityFromScheduler = false;
51 export const enableDiscreteEventFlushingChange = false;
52 -export const enableEagerRootListeners = true;
52
53 export const enableDoubleInvokingEffects = false;
54
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -48,7 +48,6 @@ export const enableNewReconciler = false;
48 export const deferRenderPhaseUpdateToNextBatch = true;
49 export const decoupleUpdatePriorityFromScheduler = false;
50 export const enableDiscreteEventFlushingChange = false;
51 -export const enableEagerRootListeners = true;
51
52 export const enableDoubleInvokingEffects = false;
53
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-1
@@ -48,7 +48,6 @@ export const enableNewReconciler = false;
48 export const deferRenderPhaseUpdateToNextBatch = true;
49 export const decoupleUpdatePriorityFromScheduler = false;
50 export const enableDiscreteEventFlushingChange = false;
51 -export const enableEagerRootListeners = true;
51
52 export const enableDoubleInvokingEffects = false;
53
packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
-1
@@ -48,7 +48,6 @@ export const enableNewReconciler = false;
48 export const deferRenderPhaseUpdateToNextBatch = true;
49 export const decoupleUpdatePriorityFromScheduler = false;
50 export const enableDiscreteEventFlushingChange = false;
51 -export const enableEagerRootListeners = true;
51
52 export const enableDoubleInvokingEffects = false;
53
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-1
@@ -48,7 +48,6 @@ export const enableNewReconciler = false;
48 export const deferRenderPhaseUpdateToNextBatch = true;
49 export const decoupleUpdatePriorityFromScheduler = false;
50 export const enableDiscreteEventFlushingChange = false;
51 -export const enableEagerRootListeners = true;
51
52 export const enableDoubleInvokingEffects = false;
53
packages/shared/forks/ReactFeatureFlags.testing.js
-1
@@ -48,7 +48,6 @@ export const enableNewReconciler = false;
48 export const deferRenderPhaseUpdateToNextBatch = true;
49 export const decoupleUpdatePriorityFromScheduler = false;
50 export const enableDiscreteEventFlushingChange = false;
51 -export const enableEagerRootListeners = true;
51
52 export const enableDoubleInvokingEffects = false;
53
packages/shared/forks/ReactFeatureFlags.testing.www.js
-1
@@ -48,7 +48,6 @@ export const enableNewReconciler = false;
48 export const deferRenderPhaseUpdateToNextBatch = true;
49 export const decoupleUpdatePriorityFromScheduler = false;
50 export const enableDiscreteEventFlushingChange = true;
51 -export const enableEagerRootListeners = true;
51
52 export const enableDoubleInvokingEffects = false;
53
packages/shared/forks/ReactFeatureFlags.www-dynamic.js
-1
@@ -19,7 +19,6 @@ export const enableFilterEmptyStringAttributesDOM = __VARIANT__;
19 export const enableLegacyFBSupport = __VARIANT__;
20 export const decoupleUpdatePriorityFromScheduler = __VARIANT__;
21 export const skipUnmountedBoundaries = __VARIANT__;
22 -export const enableEagerRootListeners = !__VARIANT__;
22
23 // Enable this flag to help with concurrent mode debugging.
24 // It logs information to the console about React scheduling, rendering, and commit phases.
packages/shared/forks/ReactFeatureFlags.www.js
-1
@@ -27,7 +27,6 @@ export const {
27 decoupleUpdatePriorityFromScheduler,
28 enableDebugTracing,
29 skipUnmountedBoundaries,
30 - enableEagerRootListeners,
30 enableDoubleInvokingEffects,
31 } = dynamicFeatureFlags;
32