From 993ca533b42756811731f6b7791ae06a35ee6b4d Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 8 Oct 2020 19:32:28 +0100 Subject: [PATCH] Enable eager listeners statically (#19983) --- .../src/__tests__/ReactDOMFiber-test.js | 1 - .../react-dom/src/client/ReactDOMComponent.js | 88 +---------------- .../src/client/ReactDOMEventHandle.js | 87 +---------------- .../src/client/ReactDOMHostConfig.js | 12 +-- packages/react-dom/src/client/ReactDOMRoot.js | 31 +----- .../src/events/DOMPluginEventSystem.js | 97 ++++--------------- .../src/events/ReactDOMEventListener.js | 19 ++-- .../src/events/ReactDOMEventReplaying.js | 46 +-------- .../DOMPluginEventSystem-test.internal.js | 2 +- .../src/events/plugins/SelectEventPlugin.js | 23 +---- .../__tests__/SimpleEventPlugin-test.js | 40 ++------ packages/shared/ReactFeatureFlags.js | 2 - .../forks/ReactFeatureFlags.native-fb.js | 1 - .../forks/ReactFeatureFlags.native-oss.js | 1 - .../forks/ReactFeatureFlags.test-renderer.js | 1 - .../ReactFeatureFlags.test-renderer.native.js | 1 - .../ReactFeatureFlags.test-renderer.www.js | 1 - .../shared/forks/ReactFeatureFlags.testing.js | 1 - .../forks/ReactFeatureFlags.testing.www.js | 1 - .../forks/ReactFeatureFlags.www-dynamic.js | 1 - .../shared/forks/ReactFeatureFlags.www.js | 1 - 21 files changed, 55 insertions(+), 402 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMFiber-test.js b/packages/react-dom/src/__tests__/ReactDOMFiber-test.js index 130c06b443c67..ec3ec75ce482a 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFiber-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFiber-test.js @@ -1040,7 +1040,6 @@ describe('ReactDOMFiber', () => { expect(ops).toEqual([]); }); - // @gate enableEagerRootListeners it('listens to events that do not exist in the Portal subtree', () => { const onClick = jest.fn(); diff --git a/packages/react-dom/src/client/ReactDOMComponent.js b/packages/react-dom/src/client/ReactDOMComponent.js index 8f0e3302327c8..86789230ca0f4 100644 --- a/packages/react-dom/src/client/ReactDOMComponent.js +++ b/packages/react-dom/src/client/ReactDOMComponent.js @@ -61,12 +61,7 @@ import { shouldRemoveAttribute, } from '../shared/DOMProperty'; import assertValidProps from '../shared/assertValidProps'; -import { - DOCUMENT_NODE, - ELEMENT_NODE, - COMMENT_NODE, - DOCUMENT_FRAGMENT_NODE, -} from '../shared/HTMLNodeType'; +import {DOCUMENT_NODE} from '../shared/HTMLNodeType'; import isCustomComponent from '../shared/isCustomComponent'; import possibleStandardNames from '../shared/possibleStandardNames'; import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook'; @@ -74,12 +69,8 @@ import {validateProperties as validateInputProperties} from '../shared/ReactDOMN import {validateProperties as validateUnknownProperties} from '../shared/ReactDOMUnknownPropertyHook'; import {REACT_OPAQUE_ID_TYPE} from 'shared/ReactSymbols'; +import {enableTrustedTypesIntegration} from 'shared/ReactFeatureFlags'; import { - enableTrustedTypesIntegration, - enableEagerRootListeners, -} from 'shared/ReactFeatureFlags'; -import { - listenToReactEvent, mediaEventTypes, listenToNonDelegatedEvent, } from '../events/DOMPluginEventSystem'; @@ -253,39 +244,6 @@ if (__DEV__) { }; } -export function ensureListeningTo( - rootContainerInstance: Element | Node, - reactPropEvent: string, - targetElement: Element | null, -): void { - if (!enableEagerRootListeners) { - // If we have a comment node, then use the parent node, - // which should be an element. - const rootContainerElement = - rootContainerInstance.nodeType === COMMENT_NODE - ? rootContainerInstance.parentNode - : rootContainerInstance; - if (__DEV__) { - if ( - rootContainerElement == null || - (rootContainerElement.nodeType !== ELEMENT_NODE && - // This is to support rendering into a ShadowRoot: - rootContainerElement.nodeType !== DOCUMENT_FRAGMENT_NODE) - ) { - console.error( - 'ensureListeningTo(): received a container that was not an element node. ' + - 'This is likely a bug in React. Please file an issue.', - ); - } - } - listenToReactEvent( - reactPropEvent, - ((rootContainerElement: any): Element), - targetElement, - ); - } -} - function getOwnerDocumentFromRootContainer( rootContainerElement: Element | Document, ): Document { @@ -364,9 +322,7 @@ function setInitialDOMProperties( if (__DEV__ && typeof nextProp !== 'function') { warnForInvalidEventListener(propKey, nextProp); } - if (!enableEagerRootListeners) { - ensureListeningTo(rootContainerElement, propKey, domElement); - } else if (propKey === 'onScroll') { + if (propKey === 'onScroll') { listenToNonDelegatedEvent('scroll', domElement); } } @@ -577,11 +533,6 @@ export function setInitialProperties( // We listen to this event in case to ensure emulated bubble // listeners still fire for the invalid event. listenToNonDelegatedEvent('invalid', domElement); - if (!enableEagerRootListeners) { - // For controlled components we always need to ensure we're listening - // to onChange. Even if there is no listener. - ensureListeningTo(rootContainerElement, 'onChange', domElement); - } break; case 'option': ReactDOMOptionValidateProps(domElement, rawProps); @@ -593,11 +544,6 @@ export function setInitialProperties( // We listen to this event in case to ensure emulated bubble // listeners still fire for the invalid event. listenToNonDelegatedEvent('invalid', domElement); - if (!enableEagerRootListeners) { - // For controlled components we always need to ensure we're listening - // to onChange. Even if there is no listener. - ensureListeningTo(rootContainerElement, 'onChange', domElement); - } break; case 'textarea': ReactDOMTextareaInitWrapperState(domElement, rawProps); @@ -605,11 +551,6 @@ export function setInitialProperties( // We listen to this event in case to ensure emulated bubble // listeners still fire for the invalid event. listenToNonDelegatedEvent('invalid', domElement); - if (!enableEagerRootListeners) { - // For controlled components we always need to ensure we're listening - // to onChange. Even if there is no listener. - ensureListeningTo(rootContainerElement, 'onChange', domElement); - } break; default: props = rawProps; @@ -827,9 +768,7 @@ export function diffProperties( if (__DEV__ && typeof nextProp !== 'function') { warnForInvalidEventListener(propKey, nextProp); } - if (!enableEagerRootListeners) { - ensureListeningTo(rootContainerElement, propKey, domElement); - } else if (propKey === 'onScroll') { + if (propKey === 'onScroll') { listenToNonDelegatedEvent('scroll', domElement); } } @@ -983,11 +922,6 @@ export function diffHydratedProperties( // We listen to this event in case to ensure emulated bubble // listeners still fire for the invalid event. listenToNonDelegatedEvent('invalid', domElement); - if (!enableEagerRootListeners) { - // For controlled components we always need to ensure we're listening - // to onChange. Even if there is no listener. - ensureListeningTo(rootContainerElement, 'onChange', domElement); - } break; case 'option': ReactDOMOptionValidateProps(domElement, rawProps); @@ -997,22 +931,12 @@ export function diffHydratedProperties( // We listen to this event in case to ensure emulated bubble // listeners still fire for the invalid event. listenToNonDelegatedEvent('invalid', domElement); - if (!enableEagerRootListeners) { - // For controlled components we always need to ensure we're listening - // to onChange. Even if there is no listener. - ensureListeningTo(rootContainerElement, 'onChange', domElement); - } break; case 'textarea': ReactDOMTextareaInitWrapperState(domElement, rawProps); // We listen to this event in case to ensure emulated bubble // listeners still fire for the invalid event. listenToNonDelegatedEvent('invalid', domElement); - if (!enableEagerRootListeners) { - // For controlled components we always need to ensure we're listening - // to onChange. Even if there is no listener. - ensureListeningTo(rootContainerElement, 'onChange', domElement); - } break; } @@ -1079,9 +1003,7 @@ export function diffHydratedProperties( if (__DEV__ && typeof nextProp !== 'function') { warnForInvalidEventListener(propKey, nextProp); } - if (!enableEagerRootListeners) { - ensureListeningTo(rootContainerElement, propKey, domElement); - } else if (propKey === 'onScroll') { + if (propKey === 'onScroll') { listenToNonDelegatedEvent('scroll', domElement); } } diff --git a/packages/react-dom/src/client/ReactDOMEventHandle.js b/packages/react-dom/src/client/ReactDOMEventHandle.js index ed227252e153a..69a2eb4851f1d 100644 --- a/packages/react-dom/src/client/ReactDOMEventHandle.js +++ b/packages/react-dom/src/client/ReactDOMEventHandle.js @@ -16,23 +16,19 @@ import type { import {allNativeEvents} from '../events/EventRegistry'; import { - getClosestInstanceFromNode, getEventHandlerListeners, setEventHandlerListeners, - getFiberFromScopeInstance, doesTargetHaveEventHandle, addEventHandleToTarget, } from './ReactDOMComponentTree'; -import {ELEMENT_NODE, COMMENT_NODE} from '../shared/HTMLNodeType'; +import {ELEMENT_NODE} from '../shared/HTMLNodeType'; import {listenToNativeEvent} from '../events/DOMPluginEventSystem'; -import {HostRoot, HostPortal} from 'react-reconciler/src/ReactWorkTags'; import {IS_EVENT_HANDLE_NON_MANAGED_NODE} from '../events/EventSystemFlags'; import { enableScopeAPI, enableCreateEventHandleAPI, - enableEagerRootListeners, } from 'shared/ReactFeatureFlags'; import invariant from 'shared/invariant'; @@ -40,19 +36,6 @@ type EventHandleOptions = {| capture?: boolean, |}; -function getNearestRootOrPortalContainer(node: Fiber): null | Element { - while (node !== null) { - const tag = node.tag; - // Once we encounter a host container or root container - // we can return their DOM instance. - if (tag === HostRoot || tag === HostPortal) { - return node.stateNode.containerInfo; - } - node = node.return; - } - return null; -} - function isValidEventTarget(target: EventTarget | ReactScopeInstance): boolean { return typeof (target: Object).addEventListener === 'function'; } @@ -73,79 +56,15 @@ function createEventHandleListener( }; } -function registerEventOnNearestTargetContainer( - targetFiber: Fiber, - domEventName: DOMEventName, - isCapturePhaseListener: boolean, - targetElement: Element | null, -): void { - if (!enableEagerRootListeners) { - // If it is, find the nearest root or portal and make it - // our event handle target container. - let targetContainer = getNearestRootOrPortalContainer(targetFiber); - if (targetContainer === null) { - if (__DEV__) { - console.error( - 'ReactDOM.createEventHandle: setListener called on an target ' + - 'that did not have a corresponding root. This is likely a bug in React.', - ); - } - return; - } - if (targetContainer.nodeType === COMMENT_NODE) { - targetContainer = ((targetContainer.parentNode: any): Element); - } - listenToNativeEvent( - domEventName, - isCapturePhaseListener, - targetContainer, - targetElement, - ); - } -} - function registerReactDOMEvent( target: EventTarget | ReactScopeInstance, domEventName: DOMEventName, isCapturePhaseListener: boolean, ): void { - // Check if the target is a DOM element. if ((target: any).nodeType === ELEMENT_NODE) { - if (!enableEagerRootListeners) { - const targetElement = ((target: any): Element); - // Check if the DOM element is managed by React. - const targetFiber = getClosestInstanceFromNode(targetElement); - if (targetFiber === null) { - if (__DEV__) { - console.error( - 'ReactDOM.createEventHandle: setListener called on an element ' + - 'target that is not managed by React. Ensure React rendered the DOM element.', - ); - } - return; - } - registerEventOnNearestTargetContainer( - targetFiber, - domEventName, - isCapturePhaseListener, - targetElement, - ); - } + // Do nothing. We already attached all root listeners. } else if (enableScopeAPI && isReactScope(target)) { - if (!enableEagerRootListeners) { - const scopeTarget = ((target: any): ReactScopeInstance); - const targetFiber = getFiberFromScopeInstance(scopeTarget); - if (targetFiber === null) { - // Scope is unmounted, do not proceed. - return; - } - registerEventOnNearestTargetContainer( - targetFiber, - domEventName, - isCapturePhaseListener, - null, - ); - } + // Do nothing. We already attached all root listeners. } else if (isValidEventTarget(target)) { const eventTarget = ((target: any): EventTarget); // These are valid event targets, but they are also diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js index e5ce50add75a4..63fdb08a3ae84 100644 --- a/packages/react-dom/src/client/ReactDOMHostConfig.js +++ b/packages/react-dom/src/client/ReactDOMHostConfig.js @@ -67,13 +67,9 @@ import { enableFundamentalAPI, enableCreateEventHandleAPI, enableScopeAPI, - enableEagerRootListeners, } from 'shared/ReactFeatureFlags'; import {HostComponent, HostText} from 'react-reconciler/src/ReactWorkTags'; -import { - listenToReactEvent, - listenToAllSupportedEvents, -} from '../events/DOMPluginEventSystem'; +import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem'; export type Type = string; export type Props = { @@ -1073,11 +1069,7 @@ export function makeOpaqueHydratingObject( } export function preparePortalMount(portalInstance: Instance): void { - if (enableEagerRootListeners) { - listenToAllSupportedEvents(portalInstance); - } else { - listenToReactEvent('onMouseEnter', portalInstance, null); - } + listenToAllSupportedEvents(portalInstance); } export function prepareScopeUpdate( diff --git a/packages/react-dom/src/client/ReactDOMRoot.js b/packages/react-dom/src/client/ReactDOMRoot.js index 11ec850a4ab26..66a31328540e5 100644 --- a/packages/react-dom/src/client/ReactDOMRoot.js +++ b/packages/react-dom/src/client/ReactDOMRoot.js @@ -36,14 +36,12 @@ import { unmarkContainerAsRoot, } from './ReactDOMComponentTree'; import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem'; -import {eagerlyTrapReplayableEvents} from '../events/ReactDOMEventReplaying'; import { ELEMENT_NODE, COMMENT_NODE, DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE, } from '../shared/HTMLNodeType'; -import {ensureListeningTo} from './ReactDOMComponent'; import { createContainer, @@ -52,7 +50,6 @@ import { registerMutableSourceForHydration, } from 'react-reconciler/src/ReactFiberReconciler'; import invariant from 'shared/invariant'; -import {enableEagerRootListeners} from 'shared/ReactFeatureFlags'; import { BlockingRoot, ConcurrentRoot, @@ -133,30 +130,10 @@ function createRootImpl( null; const root = createContainer(container, tag, hydrate, hydrationCallbacks); markContainerAsRoot(root.current, container); - const containerNodeType = container.nodeType; - - if (enableEagerRootListeners) { - const rootContainerElement = - container.nodeType === COMMENT_NODE ? container.parentNode : container; - listenToAllSupportedEvents(rootContainerElement); - } else { - if (hydrate && tag !== LegacyRoot) { - const doc = - containerNodeType === DOCUMENT_NODE - ? container - : container.ownerDocument; - // We need to cast this because Flow doesn't work - // with the hoisted containerNodeType. If we inline - // it, then Flow doesn't complain. We intentionally - // hoist it to reduce code-size. - eagerlyTrapReplayableEvents(container, ((doc: any): Document)); - } else if ( - containerNodeType !== DOCUMENT_FRAGMENT_NODE && - containerNodeType !== DOCUMENT_NODE - ) { - ensureListeningTo(container, 'onMouseEnter', null); - } - } + + const rootContainerElement = + container.nodeType === COMMENT_NODE ? container.parentNode : container; + listenToAllSupportedEvents(rootContainerElement); if (mutableSources) { for (let i = 0; i < mutableSources.length; i++) { diff --git a/packages/react-dom/src/events/DOMPluginEventSystem.js b/packages/react-dom/src/events/DOMPluginEventSystem.js index 0b374b6e91d29..74b95dea3d4eb 100644 --- a/packages/react-dom/src/events/DOMPluginEventSystem.js +++ b/packages/react-dom/src/events/DOMPluginEventSystem.js @@ -21,7 +21,7 @@ import type { } from './ReactSyntheticEventType'; import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; -import {registrationNameDependencies, allNativeEvents} from './EventRegistry'; +import {allNativeEvents} from './EventRegistry'; import { IS_CAPTURE_PHASE, IS_EVENT_HANDLE_NON_MANAGED_NODE, @@ -51,7 +51,6 @@ import { enableLegacyFBSupport, enableCreateEventHandleAPI, enableScopeAPI, - enableEagerRootListeners, } from 'shared/ReactFeatureFlags'; import { invokeGuardedCallbackAndCatchFirstError, @@ -320,32 +319,30 @@ const listeningMarker = .slice(2); export function listenToAllSupportedEvents(rootContainerElement: EventTarget) { - if (enableEagerRootListeners) { - if ((rootContainerElement: any)[listeningMarker]) { - // Performance optimization: don't iterate through events - // for the same portal container or root node more than once. - // TODO: once we remove the flag, we may be able to also - // remove some of the bookkeeping maps used for laziness. - return; - } - (rootContainerElement: any)[listeningMarker] = true; - allNativeEvents.forEach(domEventName => { - if (!nonDelegatedEvents.has(domEventName)) { - listenToNativeEvent( - domEventName, - false, - ((rootContainerElement: any): Element), - null, - ); - } + if ((rootContainerElement: any)[listeningMarker]) { + // Performance optimization: don't iterate through events + // for the same portal container or root node more than once. + // TODO: once we remove the flag, we may be able to also + // remove some of the bookkeeping maps used for laziness. + return; + } + (rootContainerElement: any)[listeningMarker] = true; + allNativeEvents.forEach(domEventName => { + if (!nonDelegatedEvents.has(domEventName)) { listenToNativeEvent( domEventName, - true, + false, ((rootContainerElement: any): Element), null, ); - }); - } + } + listenToNativeEvent( + domEventName, + true, + ((rootContainerElement: any): Element), + null, + ); + }); } export function listenToNativeEvent( @@ -411,60 +408,6 @@ export function listenToNativeEvent( } } -export function listenToReactEvent( - reactEvent: string, - rootContainerElement: Element, - targetElement: Element | null, -): void { - if (!enableEagerRootListeners) { - const dependencies = registrationNameDependencies[reactEvent]; - const dependenciesLength = dependencies.length; - // If the dependencies length is 1, that means we're not using a polyfill - // plugin like ChangeEventPlugin, BeforeInputPlugin, EnterLeavePlugin - // and SelectEventPlugin. We always use the native bubble event phase for - // these plugins and emulate two phase event dispatching. SimpleEventPlugin - // always only has a single dependency and SimpleEventPlugin events also - // use either the native capture event phase or bubble event phase, there - // is no emulation (except for focus/blur, but that will be removed soon). - const isPolyfillEventPlugin = dependenciesLength !== 1; - - if (isPolyfillEventPlugin) { - const listenerSet = getEventListenerSet(rootContainerElement); - // When eager listeners are off, this Set has a dual purpose: it both - // captures which native listeners we registered (e.g. "click__bubble") - // and *React* lazy listeners (e.g. "onClick") so we don't do extra checks. - // This second usage does not exist in the eager mode. - if (!listenerSet.has(reactEvent)) { - listenerSet.add(reactEvent); - for (let i = 0; i < dependenciesLength; i++) { - listenToNativeEvent( - dependencies[i], - false, - rootContainerElement, - targetElement, - ); - } - } - } else { - const isCapturePhaseListener = - reactEvent.substr(-7) === 'Capture' && - // Edge case: onGotPointerCapture and onLostPointerCapture - // end with "Capture" but that's part of their event names. - // The Capture versions would end with CaptureCapture. - // So we have to check against that. - // This check works because none of the events we support - // end with "Pointer". - reactEvent.substr(-14, 7) !== 'Pointer'; - listenToNativeEvent( - dependencies[0], - isCapturePhaseListener, - rootContainerElement, - targetElement, - ); - } - } -} - function addTrappedEventListener( targetContainer: EventTarget, domEventName: DOMEventName, diff --git a/packages/react-dom/src/events/ReactDOMEventListener.js b/packages/react-dom/src/events/ReactDOMEventListener.js index 3aacb54873d2b..d5f13d3b0ad90 100644 --- a/packages/react-dom/src/events/ReactDOMEventListener.js +++ b/packages/react-dom/src/events/ReactDOMEventListener.js @@ -40,7 +40,6 @@ import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree'; import { enableLegacyFBSupport, - enableEagerRootListeners, decoupleUpdatePriorityFromScheduler, } from 'shared/ReactFeatureFlags'; import { @@ -188,16 +187,14 @@ export function dispatchEvent( if (!_enabled) { return; } - let allowReplay = true; - if (enableEagerRootListeners) { - // TODO: replaying capture phase events is currently broken - // because we used to do it during top-level native bubble handlers - // but now we use different bubble and capture handlers. - // In eager mode, we attach capture listeners early, so we need - // to filter them out until we fix the logic to handle them correctly. - // This could've been outside the flag but I put it inside to reduce risk. - allowReplay = (eventSystemFlags & IS_CAPTURE_PHASE) === 0; - } + + // TODO: replaying capture phase events is currently broken + // because we used to do it during top-level native bubble handlers + // but now we use different bubble and capture handlers. + // In eager mode, we attach capture listeners early, so we need + // to filter them out until we fix the logic to handle them correctly. + const allowReplay = (eventSystemFlags & IS_CAPTURE_PHASE) === 0; + if ( allowReplay && hasQueuedDiscreteEvents() && diff --git a/packages/react-dom/src/events/ReactDOMEventReplaying.js b/packages/react-dom/src/events/ReactDOMEventReplaying.js index 8b8d08f5e06a8..2fbe0e1b0b616 100644 --- a/packages/react-dom/src/events/ReactDOMEventReplaying.js +++ b/packages/react-dom/src/events/ReactDOMEventReplaying.js @@ -14,10 +14,7 @@ import type {EventSystemFlags} from './EventSystemFlags'; import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes'; import type {LanePriority} from 'react-reconciler/src/ReactFiberLane'; -import { - enableSelectiveHydration, - enableEagerRootListeners, -} from 'shared/ReactFeatureFlags'; +import {enableSelectiveHydration} from 'shared/ReactFeatureFlags'; import { unstable_runWithPriority as runWithPriority, unstable_scheduleCallback as scheduleCallback, @@ -85,7 +82,6 @@ type PointerEvent = Event & { }; import {IS_REPLAYED} from './EventSystemFlags'; -import {listenToNativeEvent} from './DOMPluginEventSystem'; type QueuedReplayableEvent = {| blockedOn: null | Container | SuspenseInstance, @@ -159,50 +155,10 @@ const discreteReplayableEvents: Array = [ 'submit', ]; -const continuousReplayableEvents: Array = [ - 'dragenter', - 'dragleave', - 'focusin', - 'focusout', - 'mouseover', - 'mouseout', - 'pointerover', - 'pointerout', - 'gotpointercapture', - 'lostpointercapture', -]; - export function isReplayableDiscreteEvent(eventType: DOMEventName): boolean { return discreteReplayableEvents.indexOf(eventType) > -1; } -function trapReplayableEventForContainer( - domEventName: DOMEventName, - container: Container, -) { - // When the flag is on, we do this in a unified codepath elsewhere. - if (!enableEagerRootListeners) { - listenToNativeEvent(domEventName, false, ((container: any): Element), null); - } -} - -export function eagerlyTrapReplayableEvents( - container: Container, - document: Document, -) { - // When the flag is on, we do this in a unified codepath elsewhere. - if (!enableEagerRootListeners) { - // Discrete - discreteReplayableEvents.forEach(domEventName => { - trapReplayableEventForContainer(domEventName, container); - }); - // Continuous - continuousReplayableEvents.forEach(domEventName => { - trapReplayableEventForContainer(domEventName, container); - }); - } -} - function createQueuedReplayableEvent( blockedOn: null | Container | SuspenseInstance, domEventName: DOMEventName, diff --git a/packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js b/packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js index a2517cba83456..6a3662962a740 100644 --- a/packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js +++ b/packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js @@ -2643,7 +2643,7 @@ describe('DOMPluginEventSystem', () => { expect(log[5]).toEqual(['bubble', buttonElement]); }); - // @gate experimental && enableEagerRootListeners + // @gate experimental it('propagates known createEventHandle events through portals without inner listeners', () => { const buttonRef = React.createRef(); const divRef = React.createRef(); diff --git a/packages/react-dom/src/events/plugins/SelectEventPlugin.js b/packages/react-dom/src/events/plugins/SelectEventPlugin.js index e1989b773b560..befa735be8788 100644 --- a/packages/react-dom/src/events/plugins/SelectEventPlugin.js +++ b/packages/react-dom/src/events/plugins/SelectEventPlugin.js @@ -16,14 +16,10 @@ import {canUseDOM} from 'shared/ExecutionEnvironment'; import {SyntheticEvent} from '../../events/SyntheticEvent'; import isTextInputElement from '../isTextInputElement'; import shallowEqual from 'shared/shallowEqual'; -import {enableEagerRootListeners} from 'shared/ReactFeatureFlags'; import {registerTwoPhaseEvent} from '../EventRegistry'; import getActiveElement from '../../client/getActiveElement'; -import { - getNodeFromInstance, - getEventListenerSet, -} from '../../client/ReactDOMComponentTree'; +import {getNodeFromInstance} from '../../client/ReactDOMComponentTree'; import {hasSelectionCapabilities} from '../../client/ReactInputSelection'; import {DOCUMENT_NODE} from '../../shared/HTMLNodeType'; import {accumulateTwoPhaseListeners} from '../DOMPluginEventSystem'; @@ -154,23 +150,6 @@ function extractEvents( eventSystemFlags: EventSystemFlags, targetContainer: EventTarget, ) { - if (!enableEagerRootListeners) { - const eventListenerSet = getEventListenerSet(targetContainer); - // Track whether all listeners exists for this plugin. If none exist, we do - // not extract events. See #3639. - if ( - // If we are handling selectionchange, then we don't need to - // check for the other dependencies, as selectionchange is only - // event attached from the onChange plugin and we don't expose an - // onSelectionChange event from React. - domEventName !== 'selectionchange' && - !eventListenerSet.has('onSelect') && - !eventListenerSet.has('onSelectCapture') - ) { - return; - } - } - const targetNode = targetInst ? getNodeFromInstance(targetInst) : window; switch (domEventName) { diff --git a/packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js b/packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js index a01bb938883c0..92ee2e369315a 100644 --- a/packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js +++ b/packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js @@ -516,36 +516,16 @@ describe('SimpleEventPlugin', function() { return nativeAddEventListener.apply(this, arguments); }; - ReactDOM.render( -
{}} - onTouchMove={() => {}} - onWheel={() => {}} - // A few events that should be unaffected: - onClick={() => {}} - onScroll={() => {}} - onTouchEnd={() => {}} - onChange={() => {}} - onPointerDown={() => {}} - onPointerMove={() => {}} - />, - container, - ); - - if (gate(flags => flags.enableEagerRootListeners)) { - expect(passiveEvents).toEqual([ - 'touchstart', - 'touchstart', - 'touchmove', - 'touchmove', - 'wheel', - 'wheel', - ]); - } else { - expect(passiveEvents).toEqual(['touchstart', 'touchmove', 'wheel']); - } + ReactDOM.render(
, container); + + expect(passiveEvents).toEqual([ + 'touchstart', + 'touchstart', + 'touchmove', + 'touchmove', + 'wheel', + 'wheel', + ]); }); }); }); diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 1302c0f6ff7f6..836b78524ef90 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -133,6 +133,4 @@ export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; -export const enableEagerRootListeners = true; - export const enableDoubleInvokingEffects = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index bd4a939be4329..82e8d4dcd7baf 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -49,7 +49,6 @@ export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = true; export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; -export const enableEagerRootListeners = true; export const enableDoubleInvokingEffects = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 7a120940842d0..7a793d16ab330 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -48,7 +48,6 @@ export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = true; export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; -export const enableEagerRootListeners = true; export const enableDoubleInvokingEffects = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 6804c1923bf81..4402523c54105 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -48,7 +48,6 @@ export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = true; export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; -export const enableEagerRootListeners = true; export const enableDoubleInvokingEffects = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js index f4ccc11a08cfe..a71ad45dbd46a 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js @@ -48,7 +48,6 @@ export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = true; export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; -export const enableEagerRootListeners = true; export const enableDoubleInvokingEffects = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index b4b7a56097c7e..c60072346eab2 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -48,7 +48,6 @@ export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = true; export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; -export const enableEagerRootListeners = true; export const enableDoubleInvokingEffects = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index f28ad3567e0e8..90c1fa268b25c 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -48,7 +48,6 @@ export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = true; export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = false; -export const enableEagerRootListeners = true; export const enableDoubleInvokingEffects = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js index a962b280a23a7..717199df1d53b 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.www.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js @@ -48,7 +48,6 @@ export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = true; export const decoupleUpdatePriorityFromScheduler = false; export const enableDiscreteEventFlushingChange = true; -export const enableEagerRootListeners = true; export const enableDoubleInvokingEffects = false; diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index 881a0e61010da..103e8adc5c72b 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -19,7 +19,6 @@ export const enableFilterEmptyStringAttributesDOM = __VARIANT__; export const enableLegacyFBSupport = __VARIANT__; export const decoupleUpdatePriorityFromScheduler = __VARIANT__; export const skipUnmountedBoundaries = __VARIANT__; -export const enableEagerRootListeners = !__VARIANT__; // Enable this flag to help with concurrent mode debugging. // It logs information to the console about React scheduling, rendering, and commit phases. diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index ea9610713d057..58d6987495f92 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -27,7 +27,6 @@ export const { decoupleUpdatePriorityFromScheduler, enableDebugTracing, skipUnmountedBoundaries, - enableEagerRootListeners, enableDoubleInvokingEffects, } = dynamicFeatureFlags;