Skip to content

Commit 993ca53

Browse files
authored
Enable eager listeners statically (#19983)
1 parent 40c52de commit 993ca53

21 files changed

+55
-402
lines changed

Diff for: packages/react-dom/src/__tests__/ReactDOMFiber-test.js

-1
Original file line numberDiff line numberDiff line change
@@ -1040,7 +1040,6 @@ describe('ReactDOMFiber', () => {
10401040
expect(ops).toEqual([]);
10411041
});
10421042

1043-
// @gate enableEagerRootListeners
10441043
it('listens to events that do not exist in the Portal subtree', () => {
10451044
const onClick = jest.fn();
10461045

Diff for: packages/react-dom/src/client/ReactDOMComponent.js

+5-83
Original file line numberDiff line numberDiff line change
@@ -61,25 +61,16 @@ import {
6161
shouldRemoveAttribute,
6262
} from '../shared/DOMProperty';
6363
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';
7065
import isCustomComponent from '../shared/isCustomComponent';
7166
import possibleStandardNames from '../shared/possibleStandardNames';
7267
import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook';
7368
import {validateProperties as validateInputProperties} from '../shared/ReactDOMNullInputValuePropHook';
7469
import {validateProperties as validateUnknownProperties} from '../shared/ReactDOMUnknownPropertyHook';
7570
import {REACT_OPAQUE_ID_TYPE} from 'shared/ReactSymbols';
7671

72+
import {enableTrustedTypesIntegration} from 'shared/ReactFeatureFlags';
7773
import {
78-
enableTrustedTypesIntegration,
79-
enableEagerRootListeners,
80-
} from 'shared/ReactFeatureFlags';
81-
import {
82-
listenToReactEvent,
8374
mediaEventTypes,
8475
listenToNonDelegatedEvent,
8576
} from '../events/DOMPluginEventSystem';
@@ -253,39 +244,6 @@ if (__DEV__) {
253244
};
254245
}
255246

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-
289247
function getOwnerDocumentFromRootContainer(
290248
rootContainerElement: Element | Document,
291249
): Document {
@@ -364,9 +322,7 @@ function setInitialDOMProperties(
364322
if (__DEV__ && typeof nextProp !== 'function') {
365323
warnForInvalidEventListener(propKey, nextProp);
366324
}
367-
if (!enableEagerRootListeners) {
368-
ensureListeningTo(rootContainerElement, propKey, domElement);
369-
} else if (propKey === 'onScroll') {
325+
if (propKey === 'onScroll') {
370326
listenToNonDelegatedEvent('scroll', domElement);
371327
}
372328
}
@@ -577,11 +533,6 @@ export function setInitialProperties(
577533
// We listen to this event in case to ensure emulated bubble
578534
// listeners still fire for the invalid event.
579535
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-
}
585536
break;
586537
case 'option':
587538
ReactDOMOptionValidateProps(domElement, rawProps);
@@ -593,23 +544,13 @@ export function setInitialProperties(
593544
// We listen to this event in case to ensure emulated bubble
594545
// listeners still fire for the invalid event.
595546
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-
}
601547
break;
602548
case 'textarea':
603549
ReactDOMTextareaInitWrapperState(domElement, rawProps);
604550
props = ReactDOMTextareaGetHostProps(domElement, rawProps);
605551
// We listen to this event in case to ensure emulated bubble
606552
// listeners still fire for the invalid event.
607553
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-
}
613554
break;
614555
default:
615556
props = rawProps;
@@ -827,9 +768,7 @@ export function diffProperties(
827768
if (__DEV__ && typeof nextProp !== 'function') {
828769
warnForInvalidEventListener(propKey, nextProp);
829770
}
830-
if (!enableEagerRootListeners) {
831-
ensureListeningTo(rootContainerElement, propKey, domElement);
832-
} else if (propKey === 'onScroll') {
771+
if (propKey === 'onScroll') {
833772
listenToNonDelegatedEvent('scroll', domElement);
834773
}
835774
}
@@ -983,11 +922,6 @@ export function diffHydratedProperties(
983922
// We listen to this event in case to ensure emulated bubble
984923
// listeners still fire for the invalid event.
985924
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-
}
991925
break;
992926
case 'option':
993927
ReactDOMOptionValidateProps(domElement, rawProps);
@@ -997,22 +931,12 @@ export function diffHydratedProperties(
997931
// We listen to this event in case to ensure emulated bubble
998932
// listeners still fire for the invalid event.
999933
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-
}
1005934
break;
1006935
case 'textarea':
1007936
ReactDOMTextareaInitWrapperState(domElement, rawProps);
1008937
// We listen to this event in case to ensure emulated bubble
1009938
// listeners still fire for the invalid event.
1010939
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-
}
1016940
break;
1017941
}
1018942

@@ -1079,9 +1003,7 @@ export function diffHydratedProperties(
10791003
if (__DEV__ && typeof nextProp !== 'function') {
10801004
warnForInvalidEventListener(propKey, nextProp);
10811005
}
1082-
if (!enableEagerRootListeners) {
1083-
ensureListeningTo(rootContainerElement, propKey, domElement);
1084-
} else if (propKey === 'onScroll') {
1006+
if (propKey === 'onScroll') {
10851007
listenToNonDelegatedEvent('scroll', domElement);
10861008
}
10871009
}

Diff for: packages/react-dom/src/client/ReactDOMEventHandle.js

+3-84
Original file line numberDiff line numberDiff line change
@@ -16,43 +16,26 @@ import type {
1616

1717
import {allNativeEvents} from '../events/EventRegistry';
1818
import {
19-
getClosestInstanceFromNode,
2019
getEventHandlerListeners,
2120
setEventHandlerListeners,
22-
getFiberFromScopeInstance,
2321
doesTargetHaveEventHandle,
2422
addEventHandleToTarget,
2523
} from './ReactDOMComponentTree';
26-
import {ELEMENT_NODE, COMMENT_NODE} from '../shared/HTMLNodeType';
24+
import {ELEMENT_NODE} from '../shared/HTMLNodeType';
2725
import {listenToNativeEvent} from '../events/DOMPluginEventSystem';
2826

29-
import {HostRoot, HostPortal} from 'react-reconciler/src/ReactWorkTags';
3027
import {IS_EVENT_HANDLE_NON_MANAGED_NODE} from '../events/EventSystemFlags';
3128

3229
import {
3330
enableScopeAPI,
3431
enableCreateEventHandleAPI,
35-
enableEagerRootListeners,
3632
} from 'shared/ReactFeatureFlags';
3733
import invariant from 'shared/invariant';
3834

3935
type EventHandleOptions = {|
4036
capture?: boolean,
4137
|};
4238

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-
5639
function isValidEventTarget(target: EventTarget | ReactScopeInstance): boolean {
5740
return typeof (target: Object).addEventListener === 'function';
5841
}
@@ -73,79 +56,15 @@ function createEventHandleListener(
7356
};
7457
}
7558

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-
10759
function registerReactDOMEvent(
10860
target: EventTarget | ReactScopeInstance,
10961
domEventName: DOMEventName,
11062
isCapturePhaseListener: boolean,
11163
): void {
112-
// Check if the target is a DOM element.
11364
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.
13466
} 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.
14968
} else if (isValidEventTarget(target)) {
15069
const eventTarget = ((target: any): EventTarget);
15170
// These are valid event targets, but they are also

Diff for: packages/react-dom/src/client/ReactDOMHostConfig.js

+2-10
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,9 @@ import {
6767
enableFundamentalAPI,
6868
enableCreateEventHandleAPI,
6969
enableScopeAPI,
70-
enableEagerRootListeners,
7170
} from 'shared/ReactFeatureFlags';
7271
import {HostComponent, HostText} from 'react-reconciler/src/ReactWorkTags';
73-
import {
74-
listenToReactEvent,
75-
listenToAllSupportedEvents,
76-
} from '../events/DOMPluginEventSystem';
72+
import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem';
7773

7874
export type Type = string;
7975
export type Props = {
@@ -1073,11 +1069,7 @@ export function makeOpaqueHydratingObject(
10731069
}
10741070

10751071
export function preparePortalMount(portalInstance: Instance): void {
1076-
if (enableEagerRootListeners) {
1077-
listenToAllSupportedEvents(portalInstance);
1078-
} else {
1079-
listenToReactEvent('onMouseEnter', portalInstance, null);
1080-
}
1072+
listenToAllSupportedEvents(portalInstance);
10811073
}
10821074

10831075
export function prepareScopeUpdate(

Diff for: packages/react-dom/src/client/ReactDOMRoot.js

+4-27
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,12 @@ import {
3636
unmarkContainerAsRoot,
3737
} from './ReactDOMComponentTree';
3838
import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem';
39-
import {eagerlyTrapReplayableEvents} from '../events/ReactDOMEventReplaying';
4039
import {
4140
ELEMENT_NODE,
4241
COMMENT_NODE,
4342
DOCUMENT_NODE,
4443
DOCUMENT_FRAGMENT_NODE,
4544
} from '../shared/HTMLNodeType';
46-
import {ensureListeningTo} from './ReactDOMComponent';
4745

4846
import {
4947
createContainer,
@@ -52,7 +50,6 @@ import {
5250
registerMutableSourceForHydration,
5351
} from 'react-reconciler/src/ReactFiberReconciler';
5452
import invariant from 'shared/invariant';
55-
import {enableEagerRootListeners} from 'shared/ReactFeatureFlags';
5653
import {
5754
BlockingRoot,
5855
ConcurrentRoot,
@@ -133,30 +130,10 @@ function createRootImpl(
133130
null;
134131
const root = createContainer(container, tag, hydrate, hydrationCallbacks);
135132
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);
160137

161138
if (mutableSources) {
162139
for (let i = 0; i < mutableSources.length; i++) {

0 commit comments

Comments
 (0)