Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modern Event System: Add scaffolding for createEventHandle #18898

Merged
merged 1 commit into from May 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/react-art/src/ReactARTHostConfig.js
Expand Up @@ -473,7 +473,7 @@ export function getInstanceFromNode(node) {
throw new Error('Not yet implemented.');
}

export function beforeRemoveInstance(instance) {
export function removeInstanceEventHandles(instance) {
// noop
}

Expand Down
15 changes: 15 additions & 0 deletions packages/react-dom/src/client/ReactDOMComponentTree.js
Expand Up @@ -9,6 +9,7 @@

import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import type {ReactScopeInstance} from 'shared/ReactTypes';
import type {ReactDOMEventHandleListener} from '../shared/ReactDOMTypes';
import type {
Container,
TextInstance,
Expand Down Expand Up @@ -37,6 +38,7 @@ const internalInstanceKey = '__reactFiber$' + randomKey;
const internalPropsKey = '__reactProps$' + randomKey;
const internalContainerInstanceKey = '__reactContainer$' + randomKey;
const internalEventHandlersKey = '__reactEvents$' + randomKey;
const internalEventHandlerListenersKey = '__reactListeners$' + randomKey;

export type ElementListenerMap = Map<
DOMTopLevelEventType | string,
Expand Down Expand Up @@ -217,3 +219,16 @@ export function getFiberFromScopeInstance(
}
return null;
}

export function setEventHandlerListeners(
scope: EventTarget | ReactScopeInstance,
listeners: Set<ReactDOMEventHandleListener>,
): void {
(scope: any)[internalEventHandlerListenersKey] = listeners;
}

export function getEventHandlerListeners(
scope: EventTarget | ReactScopeInstance,
): null | Set<ReactDOMEventHandleListener> {
return (scope: any)[internalEventHandlerListenersKey] || null;
}
15 changes: 8 additions & 7 deletions packages/react-dom/src/client/ReactDOMHostConfig.js
Expand Up @@ -77,6 +77,7 @@ import {
enableFundamentalAPI,
enableModernEventSystem,
enableScopeAPI,
enableCreateEventHandleAPI,
} from 'shared/ReactFeatureFlags';
import {HostComponent, HostText} from 'react-reconciler/src/ReactWorkTags';
import {TOP_BEFORE_BLUR, TOP_AFTER_BLUR} from '../events/DOMTopLevelEventTypes';
Expand Down Expand Up @@ -233,7 +234,7 @@ export function prepareForCommit(containerInfo: Container): Object | null {
eventsEnabled = ReactBrowserEventEmitterIsEnabled();
selectionInformation = getSelectionInformation();
let activeInstance = null;
if (enableDeprecatedFlareAPI) {
if (enableDeprecatedFlareAPI || enableCreateEventHandleAPI) {
const focusedElem = selectionInformation.focusedElem;
if (focusedElem !== null) {
activeInstance = getClosestInstanceFromNode(focusedElem);
Expand All @@ -244,15 +245,15 @@ export function prepareForCommit(containerInfo: Container): Object | null {
}

export function beforeActiveInstanceBlur(): void {
if (enableDeprecatedFlareAPI) {
if (enableDeprecatedFlareAPI || enableCreateEventHandleAPI) {
ReactBrowserEventEmitterSetEnabled(true);
dispatchBeforeDetachedBlur((selectionInformation: any).focusedElem);
ReactBrowserEventEmitterSetEnabled(false);
}
}

export function afterActiveInstanceBlur(): void {
if (enableDeprecatedFlareAPI) {
if (enableDeprecatedFlareAPI || enableCreateEventHandleAPI) {
ReactBrowserEventEmitterSetEnabled(true);
dispatchAfterDetachedBlur((selectionInformation: any).focusedElem);
ReactBrowserEventEmitterSetEnabled(false);
Expand Down Expand Up @@ -515,7 +516,7 @@ function createEvent(type: TopLevelType): Event {
}

function dispatchBeforeDetachedBlur(target: HTMLElement): void {
if (enableDeprecatedFlareAPI) {
if (enableDeprecatedFlareAPI || enableCreateEventHandleAPI) {
const event = createEvent(TOP_BEFORE_BLUR);
// Dispatch "beforeblur" directly on the target,
// so it gets picked up by the event system and
Expand All @@ -525,7 +526,7 @@ function dispatchBeforeDetachedBlur(target: HTMLElement): void {
}

function dispatchAfterDetachedBlur(target: HTMLElement): void {
if (enableDeprecatedFlareAPI) {
if (enableDeprecatedFlareAPI || enableCreateEventHandleAPI) {
const event = createEvent(TOP_AFTER_BLUR);
// So we know what was detached, make the relatedTarget the
// detached target on the "afterblur" event.
Expand All @@ -535,7 +536,7 @@ function dispatchAfterDetachedBlur(target: HTMLElement): void {
}
}

export function beforeRemoveInstance(
export function removeInstanceEventHandles(
instance: Instance | TextInstance | SuspenseInstance,
) {
// TODO for ReactDOM.createEventInstance
Expand Down Expand Up @@ -1135,7 +1136,7 @@ export function prepareScopeUpdate(
}
}

export function prepareScopeUnmount(scopeInstance: Object): void {
export function removeScopeEventHandles(scopeInstance: Object): void {
// TODO when we add createEventHandle
}

Expand Down
11 changes: 10 additions & 1 deletion packages/react-dom/src/events/DOMEventProperties.js
Expand Up @@ -24,6 +24,8 @@ import {
ContinuousEvent,
} from 'shared/ReactTypes';

import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';

// Needed for SimpleEventPlugin, rather than
// do it in two places, which duplicates logic
// and increases the bundle size, we do it all
Expand Down Expand Up @@ -95,6 +97,13 @@ const otherDiscreteEvents = [
DOMTopLevelEventTypes.TOP_COMPOSITION_UPDATE,
];

if (enableCreateEventHandleAPI) {
otherDiscreteEvents.push(
DOMTopLevelEventTypes.TOP_BEFORE_BLUR,
DOMTopLevelEventTypes.TOP_AFTER_BLUR,
);
}

// prettier-ignore
const userBlockingPairsForSimpleEventPlugin = [
DOMTopLevelEventTypes.TOP_DRAG, 'drag',
Expand Down Expand Up @@ -236,7 +245,7 @@ export function getEventPriorityForListenerSystem(
}
if (__DEV__) {
console.warn(
'The event "type" provided to useEvent() does not have a known priority type.' +
'The event "type" provided to createEventHandle() does not have a known priority type.' +
' It is recommended to provide a "priority" option to specify a priority.',
);
}
Expand Down
83 changes: 59 additions & 24 deletions packages/react-dom/src/events/DOMModernPluginEventSystem.js
Expand Up @@ -32,7 +32,6 @@ import {
LEGACY_FB_SUPPORT,
IS_REPLAYED,
IS_TARGET_PHASE_ONLY,
USE_EVENT_SYSTEM,
} from './EventSystemFlags';

import {
Expand Down Expand Up @@ -78,6 +77,8 @@ import {
TOP_PLAYING,
TOP_CLICK,
TOP_SELECTION_CHANGE,
TOP_BEFORE_BLUR,
TOP_AFTER_BLUR,
getRawEventName,
} from './DOMTopLevelEventTypes';
import {
Expand All @@ -89,7 +90,10 @@ import {batchedEventUpdates} from './ReactDOMUpdateBatching';
import getListener from './getListener';
import {passiveBrowserEventsSupported} from './checkPassiveEvents';

import {enableLegacyFBSupport} from 'shared/ReactFeatureFlags';
import {
enableLegacyFBSupport,
enableCreateEventHandleAPI,
} from 'shared/ReactFeatureFlags';
import {
invokeGuardedCallbackAndCatchFirstError,
rethrowCaughtError,
Expand All @@ -99,9 +103,11 @@ import {
removeEventListener,
addEventCaptureListener,
addEventBubbleListener,
addEventBubbleListenerWithPassiveFlag,
addEventCaptureListenerWithPassiveFlag,
} from './EventListener';

const capturePhaseEvents = new Set([
export const capturePhaseEvents: Set<DOMTopLevelEventType> = new Set([
TOP_FOCUS,
TOP_BLUR,
TOP_SCROLL,
Expand Down Expand Up @@ -137,6 +143,11 @@ const capturePhaseEvents = new Set([
TOP_WAITING,
]);

if (enableCreateEventHandleAPI) {
capturePhaseEvents.add(TOP_BEFORE_BLUR);
capturePhaseEvents.add(TOP_AFTER_BLUR);
}

function executeDispatch(
event: ReactSyntheticEvent,
listener: Function,
Expand Down Expand Up @@ -217,7 +228,7 @@ function dispatchEventsForPlugins(

export function listenToTopLevelEvent(
topLevelType: DOMTopLevelEventType,
targetContainer: EventTarget,
target: EventTarget,
listenerMap: ElementListenerMap,
eventSystemFlags: EventSystemFlags,
passive?: boolean,
Expand All @@ -228,25 +239,26 @@ export function listenToTopLevelEvent(
// otherwise it won't capture incoming events that are only
// triggered on the document directly.
if (topLevelType === TOP_SELECTION_CHANGE) {
targetContainer = (targetContainer: any).ownerDocument || targetContainer;
listenerMap = getEventListenerMap(targetContainer);
target = (target: any).ownerDocument || target;
listenerMap = getEventListenerMap(target);
}
capture =
capture === undefined ? capturePhaseEvents.has(topLevelType) : capture;
const listenerMapKey = getListenerMapKey(topLevelType, capture);
const listenerEntry: ElementListenerMapEntry | void = listenerMap.get(
topLevelType,
listenerMapKey,
);
const isCapturePhase =
capture === undefined ? capturePhaseEvents.has(topLevelType) : capture;
if (listenerEntry === undefined) {
const listener = addTrappedEventListener(
targetContainer,
target,
topLevelType,
eventSystemFlags,
isCapturePhase,
capture,
false,
passive,
priority,
);
listenerMap.set(topLevelType, {passive, listener});
listenerMap.set(listenerMapKey, {passive, listener});
}
}

Expand Down Expand Up @@ -324,17 +336,35 @@ function addTrappedEventListener(
};
}
if (capture) {
unsubscribeListener = addEventCaptureListener(
targetContainer,
rawEventName,
listener,
);
if (enableCreateEventHandleAPI && passive !== undefined) {
unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
targetContainer,
rawEventName,
listener,
passive,
);
} else {
unsubscribeListener = addEventCaptureListener(
targetContainer,
rawEventName,
listener,
);
}
} else {
unsubscribeListener = addEventBubbleListener(
targetContainer,
rawEventName,
listener,
);
if (enableCreateEventHandleAPI && passive !== undefined) {
unsubscribeListener = addEventBubbleListenerWithPassiveFlag(
targetContainer,
rawEventName,
listener,
passive,
);
} else {
unsubscribeListener = addEventBubbleListener(
targetContainer,
rawEventName,
listener,
);
}
}
return unsubscribeListener;
}
Expand Down Expand Up @@ -397,8 +427,6 @@ export function dispatchEventForPluginEventSystem(
(eventSystemFlags & LEGACY_FB_SUPPORT) === 0 &&
// We also don't want to defer during event replaying.
(eventSystemFlags & IS_REPLAYED) === 0 &&
// We don't want to apply the legacy FB support for the useEvent API.
(eventSystemFlags & USE_EVENT_SYSTEM) === 0 &&
willDeferLaterForLegacyFBSupport(topLevelType, targetContainer)
) {
return;
Expand Down Expand Up @@ -707,3 +735,10 @@ export function accumulateEnterLeaveListeners(
);
}
}

export function getListenerMapKey(
topLevelType: DOMTopLevelEventType,
capture: boolean,
): string {
return `${getRawEventName(topLevelType)}__${capture ? 'capture' : 'bubble'}`;
trueadm marked this conversation as resolved.
Show resolved Hide resolved
}
13 changes: 6 additions & 7 deletions packages/react-dom/src/events/EventSystemFlags.js
Expand Up @@ -11,10 +11,9 @@ export type EventSystemFlags = number;

export const PLUGIN_EVENT_SYSTEM = 1;
export const RESPONDER_EVENT_SYSTEM = 1 << 1;
export const USE_EVENT_SYSTEM = 1 << 2;
export const IS_TARGET_PHASE_ONLY = 1 << 3;
export const IS_PASSIVE = 1 << 4;
export const PASSIVE_NOT_SUPPORTED = 1 << 5;
export const IS_REPLAYED = 1 << 6;
export const IS_FIRST_ANCESTOR = 1 << 7;
export const LEGACY_FB_SUPPORT = 1 << 8;
export const IS_TARGET_PHASE_ONLY = 1 << 2;
export const IS_PASSIVE = 1 << 3;
export const PASSIVE_NOT_SUPPORTED = 1 << 4;
export const IS_REPLAYED = 1 << 5;
export const IS_FIRST_ANCESTOR = 1 << 6;
export const LEGACY_FB_SUPPORT = 1 << 7;
14 changes: 11 additions & 3 deletions packages/react-dom/src/events/plugins/ModernSelectEventPlugin.js
Expand Up @@ -28,7 +28,11 @@ import {
} from '../../client/ReactDOMComponentTree';
import {hasSelectionCapabilities} from '../../client/ReactInputSelection';
import {DOCUMENT_NODE} from '../../shared/HTMLNodeType';
import {accumulateTwoPhaseListeners} from '../DOMModernPluginEventSystem';
import {
accumulateTwoPhaseListeners,
getListenerMapKey,
capturePhaseEvents,
} from '../DOMModernPluginEventSystem';

const skipSelectionChangeEvent =
canUseDOM && 'documentMode' in document && document.documentMode <= 11;
Expand Down Expand Up @@ -153,7 +157,9 @@ function isListeningToEvents(
const listenerMap = getEventListenerMap(mountAt);
for (let i = 0; i < events.length; i++) {
const event = events[i];
if (!listenerMap.has(event)) {
const capture = capturePhaseEvents.has(event);
const listenerMapKey = getListenerMapKey(event, capture);
if (!listenerMap.has(listenerMapKey)) {
return false;
}
}
Expand All @@ -165,7 +171,9 @@ function isListeningToEvent(
mountAt: Document | Element,
): boolean {
const listenerMap = getEventListenerMap(mountAt);
return listenerMap.has(registrationName);
const capture = capturePhaseEvents.has(registrationName);
const listenerMapKey = getListenerMapKey(registrationName, capture);
return listenerMap.has(listenerMapKey);
}

/**
Expand Down
14 changes: 13 additions & 1 deletion packages/react-dom/src/events/plugins/ModernSimpleEventPlugin.js
Expand Up @@ -26,6 +26,7 @@ import {
simpleEventPluginEventTypes,
} from '../DOMEventProperties';
import {accumulateTwoPhaseListeners} from '../DOMModernPluginEventSystem';
import {IS_TARGET_PHASE_ONLY} from '../EventSystemFlags';

import SyntheticAnimationEvent from '../SyntheticAnimationEvent';
import SyntheticClipboardEvent from '../SyntheticClipboardEvent';
Expand All @@ -40,6 +41,8 @@ import SyntheticUIEvent from '../SyntheticUIEvent';
import SyntheticWheelEvent from '../SyntheticWheelEvent';
import getEventCharCode from '../getEventCharCode';

import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';

// Only used in DEV for exhaustiveness validation.
const knownHTMLTopLevelTypes: Array<DOMTopLevelEventType> = [
DOMTopLevelEventTypes.TOP_ABORT,
Expand Down Expand Up @@ -201,7 +204,16 @@ const SimpleEventPlugin: ModernPluginModule<MouseEvent> = {
nativeEventTarget,
);

accumulateTwoPhaseListeners(targetInst, dispatchQueue, event);
if (
enableCreateEventHandleAPI &&
eventSystemFlags !== undefined &&
eventSystemFlags & IS_TARGET_PHASE_ONLY &&
targetContainer != null
) {
// TODO: accumulateEventTargetListeners
} else {
accumulateTwoPhaseListeners(targetInst, dispatchQueue, event);
}
},
};

Expand Down