Skip to content

Commit a600408

Browse files
authored
ReactDOM.useEvent: add EventTarget support (#18355)
* ReactDOM.useEvent: add support for all EventTarget types
1 parent dbd85a0 commit a600408

9 files changed

+593
-197
lines changed

packages/legacy-events/EventPluginUtils.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ if (__DEV__) {
6464
*/
6565
export function executeDispatch(event, listener, inst) {
6666
const type = event.type || 'unknown-event';
67-
event.currentTarget = getNodeFromInstance(inst);
67+
event.currentTarget =
68+
inst.tag !== undefined ? getNodeFromInstance(inst) : inst;
6869
invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
6970
event.currentTarget = null;
7071
}

packages/legacy-events/EventSystemFlags.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ export const IS_ACTIVE = 1 << 3;
1616
export const PASSIVE_NOT_SUPPORTED = 1 << 4;
1717
export const IS_REPLAYED = 1 << 5;
1818
export const IS_FIRST_ANCESTOR = 1 << 6;
19-
export const LEGACY_FB_SUPPORT = 1 << 7;
19+
export const IS_TARGET_EVENT_ONLY = 1 << 7;
20+
export const LEGACY_FB_SUPPORT = 1 << 8;

packages/legacy-events/ReactSyntheticEventType.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export type ReactSyntheticEvent = {|
3939
nativeEventTarget: EventTarget,
4040
) => ReactSyntheticEvent,
4141
isPersistent: () => boolean,
42-
_dispatchInstances: null | Array<Fiber> | Fiber,
42+
_dispatchInstances: null | Array<Fiber | EventTarget> | Fiber | EventTarget,
4343
_dispatchListeners: null | Array<Function> | Function,
4444
_targetInst: Fiber,
4545
type: string,

packages/react-dom/src/client/ReactDOMHostConfig.js

+17-25
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,13 @@ import {
7575
IS_PASSIVE,
7676
} from 'legacy-events/EventSystemFlags';
7777
import {
78-
attachElementListener,
79-
detachElementListener,
80-
isDOMDocument,
81-
isDOMElement,
78+
isManagedDOMElement,
79+
isValidEventTarget,
8280
listenToTopLevelEvent,
81+
detachListenerFromManagedDOMElement,
82+
attachListenerFromManagedDOMElement,
83+
detachTargetEventListener,
84+
attachTargetEventListener,
8385
} from '../events/DOMModernPluginEventSystem';
8486
import {getListenerMapForElement} from '../events/DOMEventListenerMap';
8587

@@ -1122,25 +1124,21 @@ export function registerEvent(
11221124
export function mountEventListener(listener: ReactDOMListener): void {
11231125
if (enableUseEventAPI) {
11241126
const {target} = listener;
1125-
if (target === window) {
1126-
// TODO (useEvent)
1127-
} else if (isDOMDocument(target)) {
1128-
// TODO (useEvent)
1129-
} else if (isDOMElement(target)) {
1130-
attachElementListener(listener);
1127+
if (isManagedDOMElement(target)) {
1128+
attachListenerFromManagedDOMElement(listener);
1129+
} else {
1130+
attachTargetEventListener(listener);
11311131
}
11321132
}
11331133
}
11341134

11351135
export function unmountEventListener(listener: ReactDOMListener): void {
11361136
if (enableUseEventAPI) {
11371137
const {target} = listener;
1138-
if (target === window) {
1139-
// TODO (useEvent)
1140-
} else if (isDOMDocument(target)) {
1141-
// TODO (useEvent)
1142-
} else if (isDOMElement(target)) {
1143-
detachElementListener(listener);
1138+
if (isManagedDOMElement(target)) {
1139+
detachListenerFromManagedDOMElement(listener);
1140+
} else {
1141+
detachTargetEventListener(listener);
11441142
}
11451143
}
11461144
}
@@ -1152,10 +1150,7 @@ export function validateEventListenerTarget(
11521150
if (enableUseEventAPI) {
11531151
if (
11541152
target != null &&
1155-
(target === window ||
1156-
isDOMDocument(target) ||
1157-
(isDOMElement(target) &&
1158-
getClosestInstanceFromNode(((target: any): Element))))
1153+
(isManagedDOMElement(target) || isValidEventTarget(target))
11591154
) {
11601155
if (listener == null || typeof listener === 'function') {
11611156
return true;
@@ -1169,11 +1164,8 @@ export function validateEventListenerTarget(
11691164
}
11701165
if (__DEV__) {
11711166
console.warn(
1172-
'Event listener method setListener() from useEvent() hook requires the first argument to be either:' +
1173-
'\n\n' +
1174-
'1. A valid DOM node that was rendered and managed by React\n' +
1175-
'2. The "window" object\n' +
1176-
'3. The "document" object',
1167+
'Event listener method setListener() from useEvent() hook requires the first argument to be ' +
1168+
'a valid DOM EventTarget. If using a ref, ensure the current value is not null.',
11771169
);
11781170
}
11791171
}

packages/react-dom/src/events/DOMEventListenerMap.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
1212
import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
1313

1414
const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
15-
// prettier-ignore
16-
const elementListenerMap:
17-
// $FlowFixMe Work around Flow bug
18-
| WeakMap
19-
| Map<EventTarget, ElementListenerMap> = new PossiblyWeakMap();
15+
// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
16+
const elementListenerMap: WeakMap<
17+
EventTarget,
18+
ElementListenerMap,
19+
> = new PossiblyWeakMap();
2020

2121
export type ElementListenerMap = Map<
2222
DOMTopLevelEventType | string,

0 commit comments

Comments
 (0)