ReactDOM.useEvent: more scaffolding changes (#18282)

This commit is contained in:
Dominic Gannaway
2020-03-12 09:12:06 +00:00
committed by GitHub
parent cd53ef5468
commit 43935eb2ea
17 changed files with 209 additions and 47 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ export type PluginModule<NativeEvent> = {
nativeTarget: NativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags: EventSystemFlags,
container?: Document | Element,
container?: EventTarget,
) => ?ReactSyntheticEvent,
tapMoveThreshold?: number,
};
@@ -34,4 +34,5 @@ export type ReactSyntheticEvent = {|
_dispatchInstances: null | Array<Fiber>,
_dispatchListeners: null | Array<Function>,
_targetInst: null | Fiber,
type: string,
|};
+12
View File
@@ -469,3 +469,15 @@ export function getInstanceFromNode(node) {
export function beforeRemoveInstance(instance) {
// noop
}
export function mountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function unmountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function validateEventListenerTarget(target: any, listener: any) {
throw new Error('Not yet implemented.');
}
+1 -1
View File
@@ -44,7 +44,7 @@ type HookLogEntry = {
type ReactDebugListenerMap = {|
clear: () => void,
setListener: (instance: EventTarget, callback: ?(Event) => void) => void,
setListener: (target: EventTarget, callback: ?(Event) => void) => void,
|};
let hookLog: Array<HookLogEntry> = [];
+65
View File
@@ -66,12 +66,17 @@ import {
enableSuspenseServerRenderer,
enableDeprecatedFlareAPI,
enableFundamentalAPI,
enableUseEventAPI,
} from 'shared/ReactFeatureFlags';
import {HostComponent} from 'shared/ReactWorkTags';
import {
RESPONDER_EVENT_SYSTEM,
IS_PASSIVE,
} from 'legacy-events/EventSystemFlags';
import {
attachElementListener,
detachElementListener,
} from '../events/DOMModernPluginEventSystem';
export type ReactListenerEvent = ReactDOMListenerEvent;
export type ReactListenerMap = ReactDOMListenerMap;
@@ -1075,3 +1080,63 @@ export function unmountFundamentalComponent(
export function getInstanceFromNode(node: HTMLElement): null | Object {
return getClosestInstanceFromNode(node) || null;
}
export function mountEventListener(listener: ReactDOMListener): void {
if (enableUseEventAPI) {
const {target} = listener;
if (target === window) {
// TODO (useEvent)
} else {
attachElementListener(listener);
}
}
}
export function unmountEventListener(listener: ReactDOMListener): void {
if (enableUseEventAPI) {
const {target} = listener;
if (target === window) {
// TODO (useEvent)
} else {
detachElementListener(listener);
}
}
}
export function validateEventListenerTarget(
target: EventTarget,
listener: ?(Event) => void,
): boolean {
if (enableUseEventAPI) {
if (
target &&
(target === window || getClosestInstanceFromNode(((target: any): Node)))
) {
if (listener == null || typeof listener === 'function') {
return true;
}
if (__DEV__) {
console.warn(
'Event listener method setListener() from useEvent() hook requires the second argument' +
' to be either a valid function callback or null/undefined.',
);
}
}
if (__DEV__) {
if (target && (target: any).nodeType === DOCUMENT_NODE) {
console.warn(
'Event listener method setListener() from useEvent() hook requires the first argument to be a valid' +
' DOM node that was rendered and managed by React or a "window" object. It looks like' +
' you supplied a "document" node, instead use the "window" object.',
);
} else {
console.warn(
'Event listener method setListener() from useEvent() hook requires the first argument to be a valid' +
' DOM node that was rendered and managed by React or a "window" object. If this is' +
' from a ref, ensure the ref value has been set before attaching.',
);
}
}
}
return false;
}
+4 -7
View File
@@ -16,18 +16,15 @@ const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
const elementListenerMap:
// $FlowFixMe Work around Flow bug
| WeakMap
| Map<
Document | Element | Node,
Map<DOMTopLevelEventType | string, null | (any => void)>,
> = new PossiblyWeakMap();
| Map<EventTarget, Map<DOMTopLevelEventType | string, null | (any => void)>> = new PossiblyWeakMap();
export function getListenerMapForElement(
element: Document | Element | Node,
target: EventTarget,
): Map<DOMTopLevelEventType | string, null | (any => void)> {
let listenerMap = elementListenerMap.get(element);
let listenerMap = elementListenerMap.get(target);
if (listenerMap === undefined) {
listenerMap = new Map();
elementListenerMap.set(element, listenerMap);
elementListenerMap.set(target, listenerMap);
}
return listenerMap;
}
+13 -4
View File
@@ -108,7 +108,7 @@ function dispatchEventsForPlugins(
eventSystemFlags: EventSystemFlags,
nativeEvent: AnyNativeEvent,
targetInst: null | Fiber,
rootContainer: Element | Document,
rootContainer: EventTarget,
): void {
const nativeEventTarget = getEventTarget(nativeEvent);
const syntheticEvents: Array<ReactSyntheticEvent> = [];
@@ -209,7 +209,7 @@ function willDeferLaterForFBLegacyPrimer(nativeEvent: any): boolean {
function isMatchingRootContainer(
grandContainer: Element,
rootContainer: Document | Element,
rootContainer: EventTarget,
): boolean {
return (
grandContainer === rootContainer ||
@@ -223,10 +223,19 @@ export function dispatchEventForPluginEventSystem(
eventSystemFlags: EventSystemFlags,
nativeEvent: AnyNativeEvent,
targetInst: null | Fiber,
rootContainer: Document | Element,
rootContainer: EventTarget,
): void {
let ancestorInst = targetInst;
if (rootContainer.nodeType !== DOCUMENT_NODE) {
// Given the rootContainer can be any EventTarget, if the
// target is that of a DOM node (other than the document)
// then we'll attempt to find the correct ancestor root.
// Note: the rootContainer can be other things like
// "window" or other valid EventTarget objects.
const possibleContainerNodeType = ((rootContainer: any): Node).nodeType;
if (
possibleContainerNodeType !== undefined &&
possibleContainerNodeType !== DOCUMENT_NODE
) {
// If we detect the FB legacy primer system, we
// defer the event to the "document" with a one
// time event listener so we can defer the event.
+17 -6
View File
@@ -8,29 +8,40 @@
*/
export function addEventBubbleListener(
element: Document | Element | Node,
target: EventTarget,
eventType: string,
listener: Function,
): void {
element.addEventListener(eventType, listener, false);
target.addEventListener(eventType, listener, false);
}
export function addEventCaptureListener(
element: Document | Element | Node,
target: EventTarget,
eventType: string,
listener: Function,
): void {
element.addEventListener(eventType, listener, true);
target.addEventListener(eventType, listener, true);
}
export function addEventCaptureListenerWithPassiveFlag(
element: Document | Element | Node,
target: EventTarget,
eventType: string,
listener: Function,
passive: boolean,
): void {
element.addEventListener(eventType, listener, {
target.addEventListener(eventType, listener, {
capture: true,
passive,
});
}
export function addEventBubbleListenerWithPassiveFlag(
target: EventTarget,
eventType: string,
listener: Function,
passive: boolean,
): void {
target.addEventListener(eventType, listener, {
passive,
});
}
+18 -10
View File
@@ -130,7 +130,7 @@ export function addResponderEventSystemEvent(
}
export function addTrappedEventListener(
container: Document | Element,
targetContainer: EventTarget,
topLevelType: DOMTopLevelEventType,
capture: boolean,
legacyFBSupport?: boolean,
@@ -153,7 +153,7 @@ export function addTrappedEventListener(
null,
topLevelType,
PLUGIN_EVENT_SYSTEM,
container,
targetContainer,
);
const rawEventName = getRawEventName(topLevelType);
@@ -179,7 +179,7 @@ export function addTrappedEventListener(
if (fbListener) {
fbListener.remove();
} else {
container.removeEventListener(
targetContainer.removeEventListener(
((rawEventName: any): string),
(listener: any),
);
@@ -188,9 +188,17 @@ export function addTrappedEventListener(
};
}
if (capture) {
fbListener = addEventCaptureListener(container, rawEventName, listener);
fbListener = addEventCaptureListener(
targetContainer,
rawEventName,
listener,
);
} else {
fbListener = addEventBubbleListener(container, rawEventName, listener);
fbListener = addEventBubbleListener(
targetContainer,
rawEventName,
listener,
);
}
// If we have an fbListener, then use that.
// We'll only have one if we use the forked
@@ -199,7 +207,7 @@ export function addTrappedEventListener(
}
export function removeTrappedPassiveEventListener(
document: Document,
targetContainer: EventTarget,
topLevelType: string,
listener: any => void,
) {
@@ -207,12 +215,12 @@ export function removeTrappedPassiveEventListener(
listener.remove();
} else {
if (passiveBrowserEventsSupported) {
document.removeEventListener(topLevelType, listener, {
targetContainer.removeEventListener(topLevelType, listener, {
capture: true,
passive: true,
});
} else {
document.removeEventListener(topLevelType, listener, true);
targetContainer.removeEventListener(topLevelType, listener, true);
}
}
}
@@ -254,7 +262,7 @@ function dispatchUserBlockingUpdate(
export function dispatchEvent(
topLevelType: DOMTopLevelEventType,
eventSystemFlags: EventSystemFlags,
container: Document | Element,
container: EventTarget,
nativeEvent: AnyNativeEvent,
): void {
if (!_enabled) {
@@ -370,7 +378,7 @@ export function dispatchEvent(
export function attemptToDispatchEvent(
topLevelType: DOMTopLevelEventType,
eventSystemFlags: EventSystemFlags,
container: Document | Element,
container: EventTarget,
nativeEvent: AnyNativeEvent,
): null | Container | SuspenseInstance {
// TODO: Warn if _enabled is false.
+5 -5
View File
@@ -126,7 +126,7 @@ type QueuedReplayableEvent = {|
topLevelType: DOMTopLevelEventType,
eventSystemFlags: EventSystemFlags,
nativeEvent: AnyNativeEvent,
container: Document | Element,
container: EventTarget,
|};
let hasScheduledReplayAttempt = false;
@@ -285,7 +285,7 @@ function createQueuedReplayableEvent(
blockedOn: null | Container | SuspenseInstance,
topLevelType: DOMTopLevelEventType,
eventSystemFlags: EventSystemFlags,
container: Document | Element,
container: EventTarget,
nativeEvent: AnyNativeEvent,
): QueuedReplayableEvent {
return {
@@ -301,7 +301,7 @@ export function queueDiscreteEvent(
blockedOn: null | Container | SuspenseInstance,
topLevelType: DOMTopLevelEventType,
eventSystemFlags: EventSystemFlags,
container: Document | Element,
container: EventTarget,
nativeEvent: AnyNativeEvent,
): void {
const queuedEvent = createQueuedReplayableEvent(
@@ -376,7 +376,7 @@ function accumulateOrCreateContinuousQueuedReplayableEvent(
blockedOn: null | Container | SuspenseInstance,
topLevelType: DOMTopLevelEventType,
eventSystemFlags: EventSystemFlags,
container: Document | Element,
container: EventTarget,
nativeEvent: AnyNativeEvent,
): QueuedReplayableEvent {
if (
@@ -411,7 +411,7 @@ export function queueIfContinuousEvent(
blockedOn: null | Container | SuspenseInstance,
topLevelType: DOMTopLevelEventType,
eventSystemFlags: EventSystemFlags,
container: Document | Element,
container: EventTarget,
nativeEvent: AnyNativeEvent,
): boolean {
// These set relatedTarget to null because the replayed event will be treated as if we
+20 -6
View File
@@ -13,29 +13,43 @@ import typeof * as EventListenerType from '../EventListener';
import typeof * as EventListenerShimType from './EventListener-www';
export function addEventBubbleListener(
element: Element,
target: EventTarget,
eventType: string,
listener: Function,
) {
return EventListenerWWW.listen(element, eventType, listener);
return EventListenerWWW.listen(target, eventType, listener);
}
export function addEventCaptureListener(
element: Element,
target: EventTarget,
eventType: string,
listener: Function,
) {
return EventListenerWWW.capture(element, eventType, listener);
return EventListenerWWW.capture(target, eventType, listener);
}
export function addEventCaptureListenerWithPassiveFlag(
element: Element,
target: EventTarget,
eventType: string,
listener: Function,
passive: boolean,
) {
return EventListenerWWW.captureWithPassiveFlag(
element,
target,
eventType,
listener,
passive,
);
}
export function addEventBubbleListenerWithPassiveFlag(
target: EventTarget,
eventType: string,
listener: Function,
passive: boolean,
) {
return EventListenerWWW.bubbleWithPassiveFlag(
target,
eventType,
listener,
passive,
@@ -473,3 +473,15 @@ export function getInstanceFromNode(node: any) {
export function beforeRemoveInstance(instance: any) {
// noop
}
export function mountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function unmountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function validateEventListenerTarget(target: any, listener: any) {
throw new Error('Not yet implemented.');
}
@@ -517,3 +517,15 @@ export function getInstanceFromNode(node: any) {
export function beforeRemoveInstance(instance: any) {
// noop
}
export function mountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function unmountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function validateEventListenerTarget(target: any, listener: any) {
throw new Error('Not yet implemented.');
}
@@ -76,6 +76,10 @@ export const shouldUpdateFundamentalComponent =
$$$hostConfig.shouldUpdateFundamentalComponent;
export const getInstanceFromNode = $$$hostConfig.getInstanceFromNode;
export const beforeRemoveInstance = $$$hostConfig.beforeRemoveInstance;
export const mountEventListener = $$$hostConfig.mountEventListener;
export const unmountEventListener = $$$hostConfig.unmountEventListener;
export const validateEventListenerTarget =
$$$hostConfig.validateEventListenerTarget;
// -------------------
// Mutation
+12
View File
@@ -379,3 +379,15 @@ export function getInstanceFromNode(mockNode: Object) {
export function beforeRemoveInstance(instance: any) {
// noop
}
export function mountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function unmountEventListener(listener: any) {
throw new Error('Not yet implemented.');
}
export function validateEventListenerTarget(target: any, listener: any) {
throw new Error('Not yet implemented.');
}
+3 -4
View File
@@ -85,13 +85,12 @@ export type ReactDOMListenerEvent = {|
export type ReactDOMListenerMap = {|
clear: () => void,
setListener: (instance: EventTarget, callback: ?(Event) => void) => void,
setListener: (target: EventTarget, callback: ?(Event) => void) => void,
|};
export type ReactDOMListener = {|
callback: Event => void,
depth: number,
destroy: Document | (Element => void),
destroy: Node => void,
event: ReactDOMListenerEvent,
instance: EventTarget,
target: EventTarget,
|};
+9 -3
View File
@@ -44,15 +44,21 @@ declare module 'ReactFiberErrorDialog' {
declare module 'EventListener' {
declare module.exports: {
listen: (
target: Element,
target: EventTarget,
type: string,
callback: Function,
priority?: number,
options?: {passive: boolean, ...},
) => mixed,
capture: (target: Element, type: string, callback: Function) => mixed,
capture: (target: EventTarget, type: string, callback: Function) => mixed,
captureWithPassiveFlag: (
target: Element,
target: EventTarget,
type: string,
callback: Function,
passive: boolean,
) => mixed,
bubbleWithPassiveFlag: (
target: EventTarget,
type: string,
callback: Function,
passive: boolean,