Move accumulateEventTargetListeners to its own module/function (#18407)

This commit is contained in:
Dominic Gannaway
2020-04-01 14:19:21 +01:00
committed by GitHub
parent dc3c6c9565
commit d6d5f5aabe
3 changed files with 162 additions and 137 deletions
+20 -1
View File
@@ -37,6 +37,9 @@ import SyntheticUIEvent from './SyntheticUIEvent';
import SyntheticWheelEvent from './SyntheticWheelEvent';
import getEventCharCode from './getEventCharCode';
import accumulateTwoPhaseListeners from './accumulateTwoPhaseListeners';
import accumulateEventTargetListeners from './accumulateEventTargetListeners';
import {IS_TARGET_EVENT_ONLY} from 'legacy-events/EventSystemFlags';
import {enableUseEventAPI} from 'shared/ReactFeatureFlags';
// Only used in DEV for exhaustiveness validation.
const knownHTMLTopLevelTypes: Array<DOMTopLevelEventType> = [
@@ -197,7 +200,23 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
nativeEvent,
nativeEventTarget,
);
accumulateTwoPhaseListeners(event, true, eventSystemFlags, targetContainer);
// For TargetEvent only accumulation, we do not traverse through
// the React tree looking for managed React DOM elements that have
// events. Instead we only check the EventTarget Store Map to see
// if the container has listeners for the particular phase we're
// interested in. This is because we attach the native event listener
// only in the given phase.
if (
enableUseEventAPI &&
eventSystemFlags !== undefined &&
eventSystemFlags & IS_TARGET_EVENT_ONLY &&
targetContainer != null
) {
accumulateEventTargetListeners(event, targetContainer);
} else {
accumulateTwoPhaseListeners(event, true);
}
return event;
},
};
@@ -0,0 +1,55 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
import {eventTargetEventListenerStore} from './DOMModernPluginEventSystem';
export default function accumulateEventTargetListeners(
event: ReactSyntheticEvent,
targetContainer: EventTarget,
): void {
const dispatchListeners = [];
const dispatchInstances = [];
const eventTypeMap = eventTargetEventListenerStore.get(targetContainer);
if (eventTypeMap !== undefined) {
const type = ((event.type: any): DOMTopLevelEventType);
const listeners = eventTypeMap.get(type);
if (listeners !== undefined) {
const isCapturePhase = (event: any).eventPhase === 1;
if (isCapturePhase) {
const captureListeners = Array.from(listeners.captured);
for (let i = captureListeners.length - 1; i >= 0; i--) {
const listener = captureListeners[i];
const {callback} = listener;
dispatchListeners.push(callback);
dispatchInstances.push(targetContainer);
}
} else {
const bubbleListeners = Array.from(listeners.bubbled);
for (let i = 0; i < bubbleListeners.length; i++) {
const listener = bubbleListeners[i];
const {callback} = listener;
dispatchListeners.push(callback);
dispatchInstances.push(targetContainer);
}
}
}
}
// To prevent allocation to the event unless we actually
// have listeners we check the length of one of the arrays.
if (dispatchListeners.length > 0) {
event._dispatchListeners = dispatchListeners;
event._dispatchInstances = dispatchInstances;
}
}
+87 -136
View File
@@ -8,7 +8,6 @@
*/
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
import type {EventSystemFlags} from 'legacy-events/EventSystemFlags';
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
import {
@@ -23,162 +22,114 @@ import {
import getListener from 'legacy-events/getListener';
import {getListenersFromTarget} from '../client/ReactDOMComponentTree';
import {IS_TARGET_EVENT_ONLY} from 'legacy-events/EventSystemFlags';
import {
eventTargetEventListenerStore,
reactScopeListenerStore,
} from './DOMModernPluginEventSystem';
import {reactScopeListenerStore} from './DOMModernPluginEventSystem';
export default function accumulateTwoPhaseListeners(
event: ReactSyntheticEvent,
accumulateUseEventListeners?: boolean,
eventSystemFlags?: EventSystemFlags,
targetContainer?: null | EventTarget,
): void {
const phasedRegistrationNames = event.dispatchConfig.phasedRegistrationNames;
const dispatchListeners = [];
const dispatchInstances = [];
// For TargetEvent only accumulation, we do not traverse through
// the React tree looking for managed React DOM elements that have
// events. Instead we only check the EventTarget Store Map to see
// if the container has listeners for the particular phase we're
// interested in. This is because we attach the native event listener
// only in the given phase.
if (
enableUseEventAPI &&
accumulateUseEventListeners &&
eventSystemFlags !== undefined &&
eventSystemFlags & IS_TARGET_EVENT_ONLY &&
targetContainer != null
) {
const eventTypeMap = eventTargetEventListenerStore.get(targetContainer);
if (eventTypeMap !== undefined) {
const type = ((event.type: any): DOMTopLevelEventType);
const listeners = eventTypeMap.get(type);
if (listeners !== undefined) {
const isCapturePhase = (event: any).eventPhase === 1;
const {bubbled, captured} = phasedRegistrationNames;
// If we are not handling EventTarget only phase, then we're doing the
// usual two phase accumulation using the React fiber tree to pick up
// all relevant useEvent and on* prop events.
let node = event._targetInst;
let lastHostComponent = null;
if (isCapturePhase) {
const captureListeners = Array.from(listeners.captured);
// Accumulate all instances and listeners via the target -> root path.
while (node !== null) {
const {stateNode: instance, tag} = node;
// Handle listeners that are on HostComponents (i.e. <div>)
if (instance !== null && tag === HostComponent) {
lastHostComponent = instance;
// For useEvent listenrs
if (
enableModernEventSystem &&
enableUseEventAPI &&
accumulateUseEventListeners
) {
// useEvent event listeners
const targetType = event.type;
const listeners = getListenersFromTarget(instance);
for (let i = captureListeners.length - 1; i >= 0; i--) {
const listener = captureListeners[i];
const {callback} = listener;
dispatchListeners.push(callback);
dispatchInstances.push(targetContainer);
}
} else {
const bubbleListeners = Array.from(listeners.bubbled);
for (let i = 0; i < bubbleListeners.length; i++) {
const listener = bubbleListeners[i];
const {callback} = listener;
dispatchListeners.push(callback);
dispatchInstances.push(targetContainer);
}
}
}
}
} else {
const {bubbled, captured} = phasedRegistrationNames;
// If we are not handling EventTarget only phase, then we're doing the
// usual two phase accumulation using the React fiber tree to pick up
// all relevant useEvent and on* prop events.
let node = event._targetInst;
let lastHostComponent = null;
// Accumulate all instances and listeners via the target -> root path.
while (node !== null) {
const {stateNode: instance, tag} = node;
// Handle listeners that are on HostComponents (i.e. <div>)
if (instance !== null && tag === HostComponent) {
lastHostComponent = instance;
// For useEvent listenrs
if (
enableModernEventSystem &&
enableUseEventAPI &&
accumulateUseEventListeners
) {
// useEvent event listeners
const targetType = event.type;
const listeners = getListenersFromTarget(instance);
if (listeners !== null) {
const listenersArr = Array.from(listeners);
for (let i = 0; i < listenersArr.length; i++) {
const listener = listenersArr[i];
const {
callback,
event: {capture, type},
} = listener;
if (type === targetType) {
if (capture === true) {
dispatchListeners.unshift(callback);
dispatchInstances.unshift(node);
} else {
dispatchListeners.push(callback);
dispatchInstances.push(node);
}
if (listeners !== null) {
const listenersArr = Array.from(listeners);
for (let i = 0; i < listenersArr.length; i++) {
const listener = listenersArr[i];
const {
callback,
event: {capture, type},
} = listener;
if (type === targetType) {
if (capture === true) {
dispatchListeners.unshift(callback);
dispatchInstances.unshift(node);
} else {
dispatchListeners.push(callback);
dispatchInstances.push(node);
}
}
}
}
// Standard React on* listeners, i.e. onClick prop
if (captured !== null) {
const captureListener = getListener(node, captured);
if (captureListener != null) {
// Capture listeners/instances should go at the start, so we
// unshift them to the start of the array.
dispatchListeners.unshift(captureListener);
dispatchInstances.unshift(node);
}
}
if (bubbled !== null) {
const bubbleListener = getListener(node, bubbled);
if (bubbleListener != null) {
// Bubble listeners/instances should go at the end, so we
// push them to the end of the array.
dispatchListeners.push(bubbleListener);
dispatchInstances.push(node);
}
}
// Standard React on* listeners, i.e. onClick prop
if (captured !== null) {
const captureListener = getListener(node, captured);
if (captureListener != null) {
// Capture listeners/instances should go at the start, so we
// unshift them to the start of the array.
dispatchListeners.unshift(captureListener);
dispatchInstances.unshift(node);
}
}
if (
enableModernEventSystem &&
enableUseEventAPI &&
enableScopeAPI &&
accumulateUseEventListeners &&
tag === ScopeComponent &&
lastHostComponent !== null
) {
const reactScope = instance.methods;
const eventTypeMap = reactScopeListenerStore.get(reactScope);
if (eventTypeMap !== undefined) {
const type = ((event.type: any): DOMTopLevelEventType);
const listeners = eventTypeMap.get(type);
if (listeners !== undefined) {
const captureListeners = Array.from(listeners.captured);
const bubbleListeners = Array.from(listeners.bubbled);
for (let i = 0; i < captureListeners.length; i++) {
const listener = captureListeners[i];
const {callback} = listener;
dispatchListeners.unshift(callback);
dispatchInstances.unshift(((lastHostComponent: any): Element));
}
for (let i = 0; i < bubbleListeners.length; i++) {
const listener = bubbleListeners[i];
const {callback} = listener;
dispatchListeners.push(callback);
dispatchInstances.push(((lastHostComponent: any): Element));
}
}
if (bubbled !== null) {
const bubbleListener = getListener(node, bubbled);
if (bubbleListener != null) {
// Bubble listeners/instances should go at the end, so we
// push them to the end of the array.
dispatchListeners.push(bubbleListener);
dispatchInstances.push(node);
}
}
node = node.return;
}
if (
enableModernEventSystem &&
enableUseEventAPI &&
enableScopeAPI &&
accumulateUseEventListeners &&
tag === ScopeComponent &&
lastHostComponent !== null
) {
const reactScope = instance.methods;
const eventTypeMap = reactScopeListenerStore.get(reactScope);
if (eventTypeMap !== undefined) {
const type = ((event.type: any): DOMTopLevelEventType);
const listeners = eventTypeMap.get(type);
if (listeners !== undefined) {
const captureListeners = Array.from(listeners.captured);
const bubbleListeners = Array.from(listeners.bubbled);
for (let i = 0; i < captureListeners.length; i++) {
const listener = captureListeners[i];
const {callback} = listener;
dispatchListeners.unshift(callback);
dispatchInstances.unshift(((lastHostComponent: any): Element));
}
for (let i = 0; i < bubbleListeners.length; i++) {
const listener = bubbleListeners[i];
const {callback} = listener;
dispatchListeners.push(callback);
dispatchInstances.push(((lastHostComponent: any): Element));
}
}
}
}
node = node.return;
}
// To prevent allocation to the event unless we actually
// have listeners we check the length of one of the arrays.
if (dispatchListeners.length > 0) {