Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 80c4dea

Browse files
authored
Modern Event System: Add scaffolding for createEventHandle (facebook#18898)
1 parent 039ad34 commit 80c4dea

25 files changed

+179
-61
lines changed

packages/react-art/src/ReactARTHostConfig.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ export function getInstanceFromNode(node) {
473473
throw new Error('Not yet implemented.');
474474
}
475475

476-
export function beforeRemoveInstance(instance) {
476+
export function removeInstanceEventHandles(instance) {
477477
// noop
478478
}
479479

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
1111
import type {ReactScopeInstance} from 'shared/ReactTypes';
12+
import type {ReactDOMEventHandleListener} from '../shared/ReactDOMTypes';
1213
import type {
1314
Container,
1415
TextInstance,
@@ -37,6 +38,7 @@ const internalInstanceKey = '__reactFiber$' + randomKey;
3738
const internalPropsKey = '__reactProps$' + randomKey;
3839
const internalContainerInstanceKey = '__reactContainer$' + randomKey;
3940
const internalEventHandlersKey = '__reactEvents$' + randomKey;
41+
const internalEventHandlerListenersKey = '__reactListeners$' + randomKey;
4042

4143
export type ElementListenerMap = Map<
4244
DOMTopLevelEventType | string,
@@ -217,3 +219,16 @@ export function getFiberFromScopeInstance(
217219
}
218220
return null;
219221
}
222+
223+
export function setEventHandlerListeners(
224+
scope: EventTarget | ReactScopeInstance,
225+
listeners: Set<ReactDOMEventHandleListener>,
226+
): void {
227+
(scope: any)[internalEventHandlerListenersKey] = listeners;
228+
}
229+
230+
export function getEventHandlerListeners(
231+
scope: EventTarget | ReactScopeInstance,
232+
): null | Set<ReactDOMEventHandleListener> {
233+
return (scope: any)[internalEventHandlerListenersKey] || null;
234+
}

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ import {
7777
enableFundamentalAPI,
7878
enableModernEventSystem,
7979
enableScopeAPI,
80+
enableCreateEventHandleAPI,
8081
} from 'shared/ReactFeatureFlags';
8182
import {HostComponent, HostText} from 'react-reconciler/src/ReactWorkTags';
8283
import {TOP_BEFORE_BLUR, TOP_AFTER_BLUR} from '../events/DOMTopLevelEventTypes';
@@ -233,7 +234,7 @@ export function prepareForCommit(containerInfo: Container): Object | null {
233234
eventsEnabled = ReactBrowserEventEmitterIsEnabled();
234235
selectionInformation = getSelectionInformation();
235236
let activeInstance = null;
236-
if (enableDeprecatedFlareAPI) {
237+
if (enableDeprecatedFlareAPI || enableCreateEventHandleAPI) {
237238
const focusedElem = selectionInformation.focusedElem;
238239
if (focusedElem !== null) {
239240
activeInstance = getClosestInstanceFromNode(focusedElem);
@@ -244,15 +245,15 @@ export function prepareForCommit(containerInfo: Container): Object | null {
244245
}
245246

246247
export function beforeActiveInstanceBlur(): void {
247-
if (enableDeprecatedFlareAPI) {
248+
if (enableDeprecatedFlareAPI || enableCreateEventHandleAPI) {
248249
ReactBrowserEventEmitterSetEnabled(true);
249250
dispatchBeforeDetachedBlur((selectionInformation: any).focusedElem);
250251
ReactBrowserEventEmitterSetEnabled(false);
251252
}
252253
}
253254

254255
export function afterActiveInstanceBlur(): void {
255-
if (enableDeprecatedFlareAPI) {
256+
if (enableDeprecatedFlareAPI || enableCreateEventHandleAPI) {
256257
ReactBrowserEventEmitterSetEnabled(true);
257258
dispatchAfterDetachedBlur((selectionInformation: any).focusedElem);
258259
ReactBrowserEventEmitterSetEnabled(false);
@@ -515,7 +516,7 @@ function createEvent(type: TopLevelType): Event {
515516
}
516517

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

527528
function dispatchAfterDetachedBlur(target: HTMLElement): void {
528-
if (enableDeprecatedFlareAPI) {
529+
if (enableDeprecatedFlareAPI || enableCreateEventHandleAPI) {
529530
const event = createEvent(TOP_AFTER_BLUR);
530531
// So we know what was detached, make the relatedTarget the
531532
// detached target on the "afterblur" event.
@@ -535,7 +536,7 @@ function dispatchAfterDetachedBlur(target: HTMLElement): void {
535536
}
536537
}
537538

538-
export function beforeRemoveInstance(
539+
export function removeInstanceEventHandles(
539540
instance: Instance | TextInstance | SuspenseInstance,
540541
) {
541542
// TODO for ReactDOM.createEventInstance
@@ -1135,7 +1136,7 @@ export function prepareScopeUpdate(
11351136
}
11361137
}
11371138

1138-
export function prepareScopeUnmount(scopeInstance: Object): void {
1139+
export function removeScopeEventHandles(scopeInstance: Object): void {
11391140
// TODO when we add createEventHandle
11401141
}
11411142

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import {
2424
ContinuousEvent,
2525
} from 'shared/ReactTypes';
2626

27+
import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';
28+
2729
// Needed for SimpleEventPlugin, rather than
2830
// do it in two places, which duplicates logic
2931
// and increases the bundle size, we do it all
@@ -95,6 +97,13 @@ const otherDiscreteEvents = [
9597
DOMTopLevelEventTypes.TOP_COMPOSITION_UPDATE,
9698
];
9799

100+
if (enableCreateEventHandleAPI) {
101+
otherDiscreteEvents.push(
102+
DOMTopLevelEventTypes.TOP_BEFORE_BLUR,
103+
DOMTopLevelEventTypes.TOP_AFTER_BLUR,
104+
);
105+
}
106+
98107
// prettier-ignore
99108
const userBlockingPairsForSimpleEventPlugin = [
100109
DOMTopLevelEventTypes.TOP_DRAG, 'drag',
@@ -236,7 +245,7 @@ export function getEventPriorityForListenerSystem(
236245
}
237246
if (__DEV__) {
238247
console.warn(
239-
'The event "type" provided to useEvent() does not have a known priority type.' +
248+
'The event "type" provided to createEventHandle() does not have a known priority type.' +
240249
' It is recommended to provide a "priority" option to specify a priority.',
241250
);
242251
}

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

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import {
3232
LEGACY_FB_SUPPORT,
3333
IS_REPLAYED,
3434
IS_TARGET_PHASE_ONLY,
35-
USE_EVENT_SYSTEM,
3635
} from './EventSystemFlags';
3736

3837
import {
@@ -78,6 +77,8 @@ import {
7877
TOP_PLAYING,
7978
TOP_CLICK,
8079
TOP_SELECTION_CHANGE,
80+
TOP_BEFORE_BLUR,
81+
TOP_AFTER_BLUR,
8182
getRawEventName,
8283
} from './DOMTopLevelEventTypes';
8384
import {
@@ -89,7 +90,10 @@ import {batchedEventUpdates} from './ReactDOMUpdateBatching';
8990
import getListener from './getListener';
9091
import {passiveBrowserEventsSupported} from './checkPassiveEvents';
9192

92-
import {enableLegacyFBSupport} from 'shared/ReactFeatureFlags';
93+
import {
94+
enableLegacyFBSupport,
95+
enableCreateEventHandleAPI,
96+
} from 'shared/ReactFeatureFlags';
9397
import {
9498
invokeGuardedCallbackAndCatchFirstError,
9599
rethrowCaughtError,
@@ -99,9 +103,11 @@ import {
99103
removeEventListener,
100104
addEventCaptureListener,
101105
addEventBubbleListener,
106+
addEventBubbleListenerWithPassiveFlag,
107+
addEventCaptureListenerWithPassiveFlag,
102108
} from './EventListener';
103109

104-
const capturePhaseEvents = new Set([
110+
export const capturePhaseEvents: Set<DOMTopLevelEventType> = new Set([
105111
TOP_FOCUS,
106112
TOP_BLUR,
107113
TOP_SCROLL,
@@ -137,6 +143,11 @@ const capturePhaseEvents = new Set([
137143
TOP_WAITING,
138144
]);
139145

146+
if (enableCreateEventHandleAPI) {
147+
capturePhaseEvents.add(TOP_BEFORE_BLUR);
148+
capturePhaseEvents.add(TOP_AFTER_BLUR);
149+
}
150+
140151
function executeDispatch(
141152
event: ReactSyntheticEvent,
142153
listener: Function,
@@ -217,7 +228,7 @@ function dispatchEventsForPlugins(
217228

218229
export function listenToTopLevelEvent(
219230
topLevelType: DOMTopLevelEventType,
220-
targetContainer: EventTarget,
231+
target: EventTarget,
221232
listenerMap: ElementListenerMap,
222233
eventSystemFlags: EventSystemFlags,
223234
passive?: boolean,
@@ -228,25 +239,26 @@ export function listenToTopLevelEvent(
228239
// otherwise it won't capture incoming events that are only
229240
// triggered on the document directly.
230241
if (topLevelType === TOP_SELECTION_CHANGE) {
231-
targetContainer = (targetContainer: any).ownerDocument || targetContainer;
232-
listenerMap = getEventListenerMap(targetContainer);
242+
target = (target: any).ownerDocument || target;
243+
listenerMap = getEventListenerMap(target);
233244
}
245+
capture =
246+
capture === undefined ? capturePhaseEvents.has(topLevelType) : capture;
247+
const listenerMapKey = getListenerMapKey(topLevelType, capture);
234248
const listenerEntry: ElementListenerMapEntry | void = listenerMap.get(
235-
topLevelType,
249+
listenerMapKey,
236250
);
237-
const isCapturePhase =
238-
capture === undefined ? capturePhaseEvents.has(topLevelType) : capture;
239251
if (listenerEntry === undefined) {
240252
const listener = addTrappedEventListener(
241-
targetContainer,
253+
target,
242254
topLevelType,
243255
eventSystemFlags,
244-
isCapturePhase,
256+
capture,
245257
false,
246258
passive,
247259
priority,
248260
);
249-
listenerMap.set(topLevelType, {passive, listener});
261+
listenerMap.set(listenerMapKey, {passive, listener});
250262
}
251263
}
252264

@@ -324,17 +336,35 @@ function addTrappedEventListener(
324336
};
325337
}
326338
if (capture) {
327-
unsubscribeListener = addEventCaptureListener(
328-
targetContainer,
329-
rawEventName,
330-
listener,
331-
);
339+
if (enableCreateEventHandleAPI && passive !== undefined) {
340+
unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
341+
targetContainer,
342+
rawEventName,
343+
listener,
344+
passive,
345+
);
346+
} else {
347+
unsubscribeListener = addEventCaptureListener(
348+
targetContainer,
349+
rawEventName,
350+
listener,
351+
);
352+
}
332353
} else {
333-
unsubscribeListener = addEventBubbleListener(
334-
targetContainer,
335-
rawEventName,
336-
listener,
337-
);
354+
if (enableCreateEventHandleAPI && passive !== undefined) {
355+
unsubscribeListener = addEventBubbleListenerWithPassiveFlag(
356+
targetContainer,
357+
rawEventName,
358+
listener,
359+
passive,
360+
);
361+
} else {
362+
unsubscribeListener = addEventBubbleListener(
363+
targetContainer,
364+
rawEventName,
365+
listener,
366+
);
367+
}
338368
}
339369
return unsubscribeListener;
340370
}
@@ -397,8 +427,6 @@ export function dispatchEventForPluginEventSystem(
397427
(eventSystemFlags & LEGACY_FB_SUPPORT) === 0 &&
398428
// We also don't want to defer during event replaying.
399429
(eventSystemFlags & IS_REPLAYED) === 0 &&
400-
// We don't want to apply the legacy FB support for the useEvent API.
401-
(eventSystemFlags & USE_EVENT_SYSTEM) === 0 &&
402430
willDeferLaterForLegacyFBSupport(topLevelType, targetContainer)
403431
) {
404432
return;
@@ -707,3 +735,10 @@ export function accumulateEnterLeaveListeners(
707735
);
708736
}
709737
}
738+
739+
export function getListenerMapKey(
740+
topLevelType: DOMTopLevelEventType,
741+
capture: boolean,
742+
): string {
743+
return `${getRawEventName(topLevelType)}__${capture ? 'capture' : 'bubble'}`;
744+
}

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@ export type EventSystemFlags = number;
1111

1212
export const PLUGIN_EVENT_SYSTEM = 1;
1313
export const RESPONDER_EVENT_SYSTEM = 1 << 1;
14-
export const USE_EVENT_SYSTEM = 1 << 2;
15-
export const IS_TARGET_PHASE_ONLY = 1 << 3;
16-
export const IS_PASSIVE = 1 << 4;
17-
export const PASSIVE_NOT_SUPPORTED = 1 << 5;
18-
export const IS_REPLAYED = 1 << 6;
19-
export const IS_FIRST_ANCESTOR = 1 << 7;
20-
export const LEGACY_FB_SUPPORT = 1 << 8;
14+
export const IS_TARGET_PHASE_ONLY = 1 << 2;
15+
export const IS_PASSIVE = 1 << 3;
16+
export const PASSIVE_NOT_SUPPORTED = 1 << 4;
17+
export const IS_REPLAYED = 1 << 5;
18+
export const IS_FIRST_ANCESTOR = 1 << 6;
19+
export const LEGACY_FB_SUPPORT = 1 << 7;

packages/react-dom/src/events/plugins/ModernSelectEventPlugin.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ import {
2828
} from '../../client/ReactDOMComponentTree';
2929
import {hasSelectionCapabilities} from '../../client/ReactInputSelection';
3030
import {DOCUMENT_NODE} from '../../shared/HTMLNodeType';
31-
import {accumulateTwoPhaseListeners} from '../DOMModernPluginEventSystem';
31+
import {
32+
accumulateTwoPhaseListeners,
33+
getListenerMapKey,
34+
capturePhaseEvents,
35+
} from '../DOMModernPluginEventSystem';
3236

3337
const skipSelectionChangeEvent =
3438
canUseDOM && 'documentMode' in document && document.documentMode <= 11;
@@ -153,7 +157,9 @@ function isListeningToEvents(
153157
const listenerMap = getEventListenerMap(mountAt);
154158
for (let i = 0; i < events.length; i++) {
155159
const event = events[i];
156-
if (!listenerMap.has(event)) {
160+
const capture = capturePhaseEvents.has(event);
161+
const listenerMapKey = getListenerMapKey(event, capture);
162+
if (!listenerMap.has(listenerMapKey)) {
157163
return false;
158164
}
159165
}
@@ -165,7 +171,9 @@ function isListeningToEvent(
165171
mountAt: Document | Element,
166172
): boolean {
167173
const listenerMap = getEventListenerMap(mountAt);
168-
return listenerMap.has(registrationName);
174+
const capture = capturePhaseEvents.has(registrationName);
175+
const listenerMapKey = getListenerMapKey(registrationName, capture);
176+
return listenerMap.has(listenerMapKey);
169177
}
170178

171179
/**

packages/react-dom/src/events/plugins/ModernSimpleEventPlugin.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
simpleEventPluginEventTypes,
2727
} from '../DOMEventProperties';
2828
import {accumulateTwoPhaseListeners} from '../DOMModernPluginEventSystem';
29+
import {IS_TARGET_PHASE_ONLY} from '../EventSystemFlags';
2930

3031
import SyntheticAnimationEvent from '../SyntheticAnimationEvent';
3132
import SyntheticClipboardEvent from '../SyntheticClipboardEvent';
@@ -40,6 +41,8 @@ import SyntheticUIEvent from '../SyntheticUIEvent';
4041
import SyntheticWheelEvent from '../SyntheticWheelEvent';
4142
import getEventCharCode from '../getEventCharCode';
4243

44+
import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';
45+
4346
// Only used in DEV for exhaustiveness validation.
4447
const knownHTMLTopLevelTypes: Array<DOMTopLevelEventType> = [
4548
DOMTopLevelEventTypes.TOP_ABORT,
@@ -201,7 +204,16 @@ const SimpleEventPlugin: ModernPluginModule<MouseEvent> = {
201204
nativeEventTarget,
202205
);
203206

204-
accumulateTwoPhaseListeners(targetInst, dispatchQueue, event);
207+
if (
208+
enableCreateEventHandleAPI &&
209+
eventSystemFlags !== undefined &&
210+
eventSystemFlags & IS_TARGET_PHASE_ONLY &&
211+
targetContainer != null
212+
) {
213+
// TODO: accumulateEventTargetListeners
214+
} else {
215+
accumulateTwoPhaseListeners(targetInst, dispatchQueue, event);
216+
}
205217
},
206218
};
207219

0 commit comments

Comments
 (0)