mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Clean up modern plugins to remove dead code (#18639)
This commit is contained in:
+310
-3
@@ -33,7 +33,12 @@ import {
|
||||
USE_EVENT_SYSTEM,
|
||||
} from './EventSystemFlags';
|
||||
|
||||
import {HostRoot, HostPortal} from 'react-reconciler/src/ReactWorkTags';
|
||||
import {
|
||||
HostRoot,
|
||||
HostPortal,
|
||||
ScopeComponent,
|
||||
HostComponent,
|
||||
} from 'react-reconciler/src/ReactWorkTags';
|
||||
|
||||
import {
|
||||
addTrappedEventListener,
|
||||
@@ -86,10 +91,12 @@ import {
|
||||
import {COMMENT_NODE} from '../shared/HTMLNodeType';
|
||||
import {topLevelEventsToDispatchConfig} from './DOMEventProperties';
|
||||
import {batchedEventUpdates} from './ReactDOMUpdateBatching';
|
||||
import getListener from './getListener';
|
||||
|
||||
import {
|
||||
enableLegacyFBSupport,
|
||||
enableUseEventAPI,
|
||||
enableScopeAPI,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
invokeGuardedCallbackAndCatchFirstError,
|
||||
@@ -153,7 +160,7 @@ const isArray = Array.isArray;
|
||||
const PossiblyWeakMap = ((typeof WeakMap === 'function' ? WeakMap : Map): any);
|
||||
|
||||
// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
|
||||
export const eventTargetEventListenerStore: WeakMap<
|
||||
const eventTargetEventListenerStore: WeakMap<
|
||||
EventTarget,
|
||||
Map<
|
||||
DOMTopLevelEventType,
|
||||
@@ -162,7 +169,7 @@ export const eventTargetEventListenerStore: WeakMap<
|
||||
> = new PossiblyWeakMap();
|
||||
|
||||
// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
|
||||
export const reactScopeListenerStore: WeakMap<
|
||||
const reactScopeListenerStore: WeakMap<
|
||||
ReactScopeMethods,
|
||||
Map<
|
||||
DOMTopLevelEventType,
|
||||
@@ -672,3 +679,303 @@ export function detachListenerFromReactScope(listener: ReactDOMListener): void {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function accumulateTwoPhaseListeners(
|
||||
event: ReactSyntheticEvent,
|
||||
accumulateUseEventListeners?: boolean,
|
||||
): void {
|
||||
const phasedRegistrationNames = event.dispatchConfig.phasedRegistrationNames;
|
||||
const dispatchListeners = [];
|
||||
const dispatchInstances: Array<Fiber | null> = [];
|
||||
const dispatchCurrentTargets = [];
|
||||
|
||||
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 instance = event._targetInst;
|
||||
let lastHostComponent = null;
|
||||
|
||||
// Accumulate all instances and listeners via the target -> root path.
|
||||
while (instance !== null) {
|
||||
const {stateNode, tag} = instance;
|
||||
// Handle listeners that are on HostComponents (i.e. <div>)
|
||||
if (tag === HostComponent && stateNode !== null) {
|
||||
const currentTarget = stateNode;
|
||||
lastHostComponent = currentTarget;
|
||||
// For useEvent listenrs
|
||||
if (enableUseEventAPI && accumulateUseEventListeners) {
|
||||
// useEvent event listeners
|
||||
const targetType = event.type;
|
||||
const listeners = getListenersFromTarget(currentTarget);
|
||||
|
||||
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(instance);
|
||||
dispatchCurrentTargets.unshift(currentTarget);
|
||||
} else {
|
||||
dispatchListeners.push(callback);
|
||||
dispatchInstances.push(instance);
|
||||
dispatchCurrentTargets.push(currentTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Standard React on* listeners, i.e. onClick prop
|
||||
if (captured !== null) {
|
||||
const captureListener = getListener(instance, 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(instance);
|
||||
dispatchCurrentTargets.unshift(currentTarget);
|
||||
}
|
||||
}
|
||||
if (bubbled !== null) {
|
||||
const bubbleListener = getListener(instance, 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(instance);
|
||||
dispatchCurrentTargets.push(currentTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
enableUseEventAPI &&
|
||||
enableScopeAPI &&
|
||||
accumulateUseEventListeners &&
|
||||
tag === ScopeComponent &&
|
||||
lastHostComponent !== null
|
||||
) {
|
||||
const reactScope = stateNode.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);
|
||||
const lastCurrentTarget = ((lastHostComponent: any): Element);
|
||||
|
||||
for (let i = 0; i < captureListeners.length; i++) {
|
||||
const listener = captureListeners[i];
|
||||
const {callback} = listener;
|
||||
dispatchListeners.unshift(callback);
|
||||
dispatchInstances.unshift(instance);
|
||||
dispatchCurrentTargets.unshift(lastCurrentTarget);
|
||||
}
|
||||
for (let i = 0; i < bubbleListeners.length; i++) {
|
||||
const listener = bubbleListeners[i];
|
||||
const {callback} = listener;
|
||||
dispatchListeners.push(callback);
|
||||
dispatchInstances.push(instance);
|
||||
dispatchCurrentTargets.push(lastCurrentTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
instance = instance.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) {
|
||||
event._dispatchListeners = dispatchListeners;
|
||||
event._dispatchInstances = dispatchInstances;
|
||||
event._dispatchCurrentTargets = dispatchCurrentTargets;
|
||||
}
|
||||
}
|
||||
|
||||
export function accumulateEventTargetListeners(
|
||||
event: ReactSyntheticEvent,
|
||||
currentTarget: EventTarget,
|
||||
): void {
|
||||
const dispatchListeners = [];
|
||||
const dispatchInstances: Array<Fiber | null> = [];
|
||||
const dispatchCurrentTargets = [];
|
||||
|
||||
const eventTypeMap = eventTargetEventListenerStore.get(currentTarget);
|
||||
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);
|
||||
// EventTarget listeners do not have instances, as there
|
||||
// is no backing Fiber instance for them (window, document etc).
|
||||
dispatchInstances.push(null);
|
||||
dispatchCurrentTargets.push(currentTarget);
|
||||
}
|
||||
} 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);
|
||||
// EventTarget listeners do not have instances, as there
|
||||
// is no backing Fiber instance for them (window, document etc).
|
||||
dispatchInstances.push(null);
|
||||
dispatchCurrentTargets.push(currentTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
event._dispatchCurrentTargets = dispatchCurrentTargets;
|
||||
}
|
||||
}
|
||||
|
||||
function getParent(inst: Fiber | null): Fiber | null {
|
||||
if (inst === null) {
|
||||
return null;
|
||||
}
|
||||
do {
|
||||
inst = inst.return;
|
||||
// TODO: If this is a HostRoot we might want to bail out.
|
||||
// That is depending on if we want nested subtrees (layers) to bubble
|
||||
// events to their parent. We could also go through parentNode on the
|
||||
// host node but that wouldn't work for React Native and doesn't let us
|
||||
// do the portal feature.
|
||||
} while (inst && inst.tag !== HostComponent);
|
||||
if (inst) {
|
||||
return inst;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the lowest common ancestor of A and B, or null if they are in
|
||||
* different trees.
|
||||
*/
|
||||
function getLowestCommonAncestor(instA: Fiber, instB: Fiber): Fiber | null {
|
||||
let nodeA = instA;
|
||||
let nodeB = instB;
|
||||
let depthA = 0;
|
||||
for (let tempA = nodeA; tempA; tempA = getParent(tempA)) {
|
||||
depthA++;
|
||||
}
|
||||
let depthB = 0;
|
||||
for (let tempB = nodeB; tempB; tempB = getParent(tempB)) {
|
||||
depthB++;
|
||||
}
|
||||
|
||||
// If A is deeper, crawl up.
|
||||
while (depthA - depthB > 0) {
|
||||
nodeA = getParent(nodeA);
|
||||
depthA--;
|
||||
}
|
||||
|
||||
// If B is deeper, crawl up.
|
||||
while (depthB - depthA > 0) {
|
||||
nodeB = getParent(nodeB);
|
||||
depthB--;
|
||||
}
|
||||
|
||||
// Walk in lockstep until we find a match.
|
||||
let depth = depthA;
|
||||
while (depth--) {
|
||||
if (nodeA === nodeB || (nodeB !== null && nodeA === nodeB.alternate)) {
|
||||
return nodeA;
|
||||
}
|
||||
nodeA = getParent(nodeA);
|
||||
nodeB = getParent(nodeB);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function accumulateEnterLeaveListenersForEvent(
|
||||
event: ReactSyntheticEvent,
|
||||
target: Fiber,
|
||||
common: Fiber | null,
|
||||
capture: boolean,
|
||||
): void {
|
||||
const registrationName = event.dispatchConfig.registrationName;
|
||||
if (registrationName === undefined) {
|
||||
return;
|
||||
}
|
||||
const dispatchListeners = [];
|
||||
const dispatchInstances: Array<Fiber | null> = [];
|
||||
const dispatchCurrentTargets = [];
|
||||
|
||||
let instance = target;
|
||||
while (instance !== null) {
|
||||
if (instance === common) {
|
||||
break;
|
||||
}
|
||||
const {alternate, stateNode, tag} = instance;
|
||||
if (alternate !== null && alternate === common) {
|
||||
break;
|
||||
}
|
||||
if (tag === HostComponent && stateNode !== null) {
|
||||
const currentTarget = stateNode;
|
||||
if (capture) {
|
||||
const captureListener = getListener(instance, registrationName);
|
||||
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(instance);
|
||||
dispatchCurrentTargets.unshift(currentTarget);
|
||||
}
|
||||
} else {
|
||||
const bubbleListener = getListener(instance, registrationName);
|
||||
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(instance);
|
||||
dispatchCurrentTargets.push(currentTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
instance = instance.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) {
|
||||
event._dispatchListeners = dispatchListeners;
|
||||
event._dispatchInstances = dispatchInstances;
|
||||
event._dispatchCurrentTargets = dispatchCurrentTargets;
|
||||
}
|
||||
}
|
||||
|
||||
export function accumulateEnterLeaveListeners(
|
||||
leaveEvent: ReactSyntheticEvent,
|
||||
enterEvent: ReactSyntheticEvent,
|
||||
from: Fiber | null,
|
||||
to: Fiber | null,
|
||||
): void {
|
||||
const common = from && to ? getLowestCommonAncestor(from, to) : null;
|
||||
|
||||
if (from !== null) {
|
||||
accumulateEnterLeaveListenersForEvent(leaveEvent, from, common, false);
|
||||
}
|
||||
if (to !== null) {
|
||||
accumulateEnterLeaveListenersForEvent(enterEvent, to, common, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
/**
|
||||
* 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 {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
|
||||
|
||||
import {HostComponent} from 'react-reconciler/src/ReactWorkTags';
|
||||
import getListener from './getListener';
|
||||
|
||||
function getParent(inst: Fiber | null): Fiber | null {
|
||||
if (inst === null) {
|
||||
return null;
|
||||
}
|
||||
do {
|
||||
inst = inst.return;
|
||||
// TODO: If this is a HostRoot we might want to bail out.
|
||||
// That is depending on if we want nested subtrees (layers) to bubble
|
||||
// events to their parent. We could also go through parentNode on the
|
||||
// host node but that wouldn't work for React Native and doesn't let us
|
||||
// do the portal feature.
|
||||
} while (inst && inst.tag !== HostComponent);
|
||||
if (inst) {
|
||||
return inst;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the lowest common ancestor of A and B, or null if they are in
|
||||
* different trees.
|
||||
*/
|
||||
function getLowestCommonAncestor(instA: Fiber, instB: Fiber): Fiber | null {
|
||||
let nodeA = instA;
|
||||
let nodeB = instB;
|
||||
let depthA = 0;
|
||||
for (let tempA = nodeA; tempA; tempA = getParent(tempA)) {
|
||||
depthA++;
|
||||
}
|
||||
let depthB = 0;
|
||||
for (let tempB = nodeB; tempB; tempB = getParent(tempB)) {
|
||||
depthB++;
|
||||
}
|
||||
|
||||
// If A is deeper, crawl up.
|
||||
while (depthA - depthB > 0) {
|
||||
nodeA = getParent(nodeA);
|
||||
depthA--;
|
||||
}
|
||||
|
||||
// If B is deeper, crawl up.
|
||||
while (depthB - depthA > 0) {
|
||||
nodeB = getParent(nodeB);
|
||||
depthB--;
|
||||
}
|
||||
|
||||
// Walk in lockstep until we find a match.
|
||||
let depth = depthA;
|
||||
while (depth--) {
|
||||
if (nodeA === nodeB || (nodeB !== null && nodeA === nodeB.alternate)) {
|
||||
return nodeA;
|
||||
}
|
||||
nodeA = getParent(nodeA);
|
||||
nodeB = getParent(nodeB);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function accumulateEnterLeaveListenersForEvent(
|
||||
event: ReactSyntheticEvent,
|
||||
target: Fiber,
|
||||
common: Fiber | null,
|
||||
capture: boolean,
|
||||
): void {
|
||||
const registrationName = event.dispatchConfig.registrationName;
|
||||
if (registrationName === undefined) {
|
||||
return;
|
||||
}
|
||||
const dispatchListeners = [];
|
||||
const dispatchInstances: Array<Fiber | null> = [];
|
||||
const dispatchCurrentTargets = [];
|
||||
|
||||
let instance = target;
|
||||
while (instance !== null) {
|
||||
if (instance === common) {
|
||||
break;
|
||||
}
|
||||
const {alternate, stateNode, tag} = instance;
|
||||
if (alternate !== null && alternate === common) {
|
||||
break;
|
||||
}
|
||||
if (tag === HostComponent && stateNode !== null) {
|
||||
const currentTarget = stateNode;
|
||||
if (capture) {
|
||||
const captureListener = getListener(instance, registrationName);
|
||||
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(instance);
|
||||
dispatchCurrentTargets.unshift(currentTarget);
|
||||
}
|
||||
} else {
|
||||
const bubbleListener = getListener(instance, registrationName);
|
||||
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(instance);
|
||||
dispatchCurrentTargets.push(currentTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
instance = instance.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) {
|
||||
event._dispatchListeners = dispatchListeners;
|
||||
event._dispatchInstances = dispatchInstances;
|
||||
event._dispatchCurrentTargets = dispatchCurrentTargets;
|
||||
}
|
||||
}
|
||||
|
||||
export default function accumulateEnterLeaveListeners(
|
||||
leaveEvent: ReactSyntheticEvent,
|
||||
enterEvent: ReactSyntheticEvent,
|
||||
from: Fiber | null,
|
||||
to: Fiber | null,
|
||||
): void {
|
||||
const common = from && to ? getLowestCommonAncestor(from, to) : null;
|
||||
|
||||
if (from !== null) {
|
||||
accumulateEnterLeaveListenersForEvent(leaveEvent, from, common, false);
|
||||
}
|
||||
if (to !== null) {
|
||||
accumulateEnterLeaveListenersForEvent(enterEvent, to, common, true);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/**
|
||||
* 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 {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
|
||||
|
||||
import {eventTargetEventListenerStore} from './DOMModernPluginEventSystem';
|
||||
|
||||
export default function accumulateEventTargetListeners(
|
||||
event: ReactSyntheticEvent,
|
||||
currentTarget: EventTarget,
|
||||
): void {
|
||||
const dispatchListeners = [];
|
||||
const dispatchInstances: Array<Fiber | null> = [];
|
||||
const dispatchCurrentTargets = [];
|
||||
|
||||
const eventTypeMap = eventTargetEventListenerStore.get(currentTarget);
|
||||
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);
|
||||
// EventTarget listeners do not have instances, as there
|
||||
// is no backing Fiber instance for them (window, document etc).
|
||||
dispatchInstances.push(null);
|
||||
dispatchCurrentTargets.push(currentTarget);
|
||||
}
|
||||
} 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);
|
||||
// EventTarget listeners do not have instances, as there
|
||||
// is no backing Fiber instance for them (window, document etc).
|
||||
dispatchInstances.push(null);
|
||||
dispatchCurrentTargets.push(currentTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
event._dispatchCurrentTargets = dispatchCurrentTargets;
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
/**
|
||||
* 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 {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
|
||||
|
||||
import {
|
||||
HostComponent,
|
||||
ScopeComponent,
|
||||
} from 'react-reconciler/src/ReactWorkTags';
|
||||
import {
|
||||
enableUseEventAPI,
|
||||
enableScopeAPI,
|
||||
enableModernEventSystem,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import getListener from './getListener';
|
||||
import {getListenersFromTarget} from '../client/ReactDOMComponentTree';
|
||||
import {reactScopeListenerStore} from './DOMModernPluginEventSystem';
|
||||
|
||||
export default function accumulateTwoPhaseListeners(
|
||||
event: ReactSyntheticEvent,
|
||||
accumulateUseEventListeners?: boolean,
|
||||
): void {
|
||||
const phasedRegistrationNames = event.dispatchConfig.phasedRegistrationNames;
|
||||
const dispatchListeners = [];
|
||||
const dispatchInstances: Array<Fiber | null> = [];
|
||||
const dispatchCurrentTargets = [];
|
||||
|
||||
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 instance = event._targetInst;
|
||||
let lastHostComponent = null;
|
||||
|
||||
// Accumulate all instances and listeners via the target -> root path.
|
||||
while (instance !== null) {
|
||||
const {stateNode, tag} = instance;
|
||||
// Handle listeners that are on HostComponents (i.e. <div>)
|
||||
if (tag === HostComponent && stateNode !== null) {
|
||||
const currentTarget = stateNode;
|
||||
lastHostComponent = currentTarget;
|
||||
// For useEvent listenrs
|
||||
if (
|
||||
enableModernEventSystem &&
|
||||
enableUseEventAPI &&
|
||||
accumulateUseEventListeners
|
||||
) {
|
||||
// useEvent event listeners
|
||||
const targetType = event.type;
|
||||
const listeners = getListenersFromTarget(currentTarget);
|
||||
|
||||
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(instance);
|
||||
dispatchCurrentTargets.unshift(currentTarget);
|
||||
} else {
|
||||
dispatchListeners.push(callback);
|
||||
dispatchInstances.push(instance);
|
||||
dispatchCurrentTargets.push(currentTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Standard React on* listeners, i.e. onClick prop
|
||||
if (captured !== null) {
|
||||
const captureListener = getListener(instance, 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(instance);
|
||||
dispatchCurrentTargets.unshift(currentTarget);
|
||||
}
|
||||
}
|
||||
if (bubbled !== null) {
|
||||
const bubbleListener = getListener(instance, 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(instance);
|
||||
dispatchCurrentTargets.push(currentTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
enableModernEventSystem &&
|
||||
enableUseEventAPI &&
|
||||
enableScopeAPI &&
|
||||
accumulateUseEventListeners &&
|
||||
tag === ScopeComponent &&
|
||||
lastHostComponent !== null
|
||||
) {
|
||||
const reactScope = stateNode.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);
|
||||
const lastCurrentTarget = ((lastHostComponent: any): Element);
|
||||
|
||||
for (let i = 0; i < captureListeners.length; i++) {
|
||||
const listener = captureListeners[i];
|
||||
const {callback} = listener;
|
||||
dispatchListeners.unshift(callback);
|
||||
dispatchInstances.unshift(instance);
|
||||
dispatchCurrentTargets.unshift(lastCurrentTarget);
|
||||
}
|
||||
for (let i = 0; i < bubbleListeners.length; i++) {
|
||||
const listener = bubbleListeners[i];
|
||||
const {callback} = listener;
|
||||
dispatchListeners.push(callback);
|
||||
dispatchInstances.push(instance);
|
||||
dispatchCurrentTargets.push(lastCurrentTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
instance = instance.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) {
|
||||
event._dispatchListeners = dispatchListeners;
|
||||
event._dispatchInstances = dispatchInstances;
|
||||
event._dispatchCurrentTargets = dispatchCurrentTargets;
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
} from '../FallbackCompositionState';
|
||||
import SyntheticCompositionEvent from '../SyntheticCompositionEvent';
|
||||
import SyntheticInputEvent from '../SyntheticInputEvent';
|
||||
import accumulateTwoPhaseListeners from '../accumulateTwoPhaseListeners';
|
||||
import {accumulateTwoPhaseListeners} from '../DOMModernPluginEventSystem';
|
||||
|
||||
const END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
|
||||
const START_KEYCODE = 229;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {runEventsInBatch} from 'legacy-events/EventBatching';
|
||||
import SyntheticEvent from 'legacy-events/SyntheticEvent';
|
||||
import isTextInputElement from '../isTextInputElement';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
@@ -27,13 +26,12 @@ import {updateValueIfChanged} from '../../client/inputValueTracking';
|
||||
import {setDefaultValue} from '../../client/ReactDOMInput';
|
||||
import {enqueueStateRestore} from '../ReactDOMControlledComponent';
|
||||
|
||||
import {
|
||||
disableInputAttributeSyncing,
|
||||
enableModernEventSystem,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import accumulateTwoPhaseListeners from '../accumulateTwoPhaseListeners';
|
||||
import {disableInputAttributeSyncing} from 'shared/ReactFeatureFlags';
|
||||
import {batchedUpdates} from '../ReactDOMUpdateBatching';
|
||||
import {dispatchEventsInBatch} from '../DOMModernPluginEventSystem';
|
||||
import {
|
||||
dispatchEventsInBatch,
|
||||
accumulateTwoPhaseListeners,
|
||||
} from '../DOMModernPluginEventSystem';
|
||||
|
||||
const eventTypes = {
|
||||
change: {
|
||||
@@ -105,11 +103,7 @@ function manualDispatchChangeEvent(nativeEvent) {
|
||||
}
|
||||
|
||||
function runEventInBatch(event) {
|
||||
if (enableModernEventSystem) {
|
||||
dispatchEventsInBatch([event]);
|
||||
} else {
|
||||
runEventsInBatch(event);
|
||||
}
|
||||
dispatchEventsInBatch([event]);
|
||||
}
|
||||
|
||||
function getInstIfValueChanged(targetInst) {
|
||||
|
||||
@@ -11,20 +11,17 @@ import {
|
||||
TOP_POINTER_OUT,
|
||||
TOP_POINTER_OVER,
|
||||
} from '../DOMTopLevelEventTypes';
|
||||
import {
|
||||
IS_REPLAYED,
|
||||
IS_FIRST_ANCESTOR,
|
||||
} from 'react-dom/src/events/EventSystemFlags';
|
||||
import {IS_REPLAYED} from 'react-dom/src/events/EventSystemFlags';
|
||||
import SyntheticMouseEvent from '../SyntheticMouseEvent';
|
||||
import SyntheticPointerEvent from '../SyntheticPointerEvent';
|
||||
import {
|
||||
getClosestInstanceFromNode,
|
||||
getNodeFromInstance,
|
||||
} from '../../client/ReactDOMComponentTree';
|
||||
import {accumulateEnterLeaveListeners} from '../DOMModernPluginEventSystem';
|
||||
|
||||
import {HostComponent, HostText} from 'react-reconciler/src/ReactWorkTags';
|
||||
import {getNearestMountedFiber} from 'react-reconciler/src/ReactFiberTreeReflection';
|
||||
import {enableModernEventSystem} from 'shared/ReactFeatureFlags';
|
||||
import accumulateEnterLeaveListeners from '../accumulateEnterLeaveListeners';
|
||||
|
||||
const eventTypes = {
|
||||
mouseEnter: {
|
||||
@@ -70,20 +67,12 @@ const EnterLeaveEventPlugin = {
|
||||
if (isOverEvent && (eventSystemFlags & IS_REPLAYED) === 0) {
|
||||
const related = nativeEvent.relatedTarget || nativeEvent.fromElement;
|
||||
if (related) {
|
||||
if (enableModernEventSystem) {
|
||||
// Due to the fact we don't add listeners to the document with the
|
||||
// modern event system and instead attach listeners to roots, we
|
||||
// need to handle the over event case. To ensure this, we just need to
|
||||
// make sure the node that we're coming from is managed by React.
|
||||
const inst = getClosestInstanceFromNode(related);
|
||||
if (inst !== null) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// If this is an over event with a target, then we've already dispatched
|
||||
// the event in the out event of the other target. If this is replayed,
|
||||
// then it's because we couldn't dispatch against this target previously
|
||||
// so we have to do it now instead.
|
||||
// Due to the fact we don't add listeners to the document with the
|
||||
// modern event system and instead attach listeners to roots, we
|
||||
// need to handle the over event case. To ensure this, we just need to
|
||||
// make sure the node that we're coming from is managed by React.
|
||||
const inst = getClosestInstanceFromNode(related);
|
||||
if (inst !== null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -176,15 +165,6 @@ const EnterLeaveEventPlugin = {
|
||||
|
||||
accumulateEnterLeaveListeners(leave, enter, from, to);
|
||||
|
||||
if (!enableModernEventSystem) {
|
||||
// If we are not processing the first ancestor, then we
|
||||
// should not process the same nativeEvent again, as we
|
||||
// will have already processed it in the first ancestor.
|
||||
if ((eventSystemFlags & IS_FIRST_ANCESTOR) === 0) {
|
||||
return [leave];
|
||||
}
|
||||
}
|
||||
|
||||
return [leave, enter];
|
||||
},
|
||||
};
|
||||
|
||||
@@ -26,7 +26,7 @@ import {getNodeFromInstance} from '../../client/ReactDOMComponentTree';
|
||||
import {hasSelectionCapabilities} from '../../client/ReactInputSelection';
|
||||
import {DOCUMENT_NODE} from '../../shared/HTMLNodeType';
|
||||
import {isListeningToAllDependencies} from '../DOMEventListenerMap';
|
||||
import accumulateTwoPhaseListeners from '../accumulateTwoPhaseListeners';
|
||||
import {accumulateTwoPhaseListeners} from '../DOMModernPluginEventSystem';
|
||||
|
||||
const skipSelectionChangeEvent =
|
||||
canUseDOM && 'documentMode' in document && document.documentMode <= 11;
|
||||
|
||||
@@ -24,6 +24,10 @@ import {
|
||||
topLevelEventsToDispatchConfig,
|
||||
simpleEventPluginEventTypes,
|
||||
} from '../DOMEventProperties';
|
||||
import {
|
||||
accumulateEventTargetListeners,
|
||||
accumulateTwoPhaseListeners,
|
||||
} from '../DOMModernPluginEventSystem';
|
||||
|
||||
import SyntheticAnimationEvent from '../SyntheticAnimationEvent';
|
||||
import SyntheticClipboardEvent from '../SyntheticClipboardEvent';
|
||||
@@ -37,8 +41,6 @@ import SyntheticTransitionEvent from '../SyntheticTransitionEvent';
|
||||
import SyntheticUIEvent from '../SyntheticUIEvent';
|
||||
import SyntheticWheelEvent from '../SyntheticWheelEvent';
|
||||
import getEventCharCode from '../getEventCharCode';
|
||||
import accumulateTwoPhaseListeners from '../accumulateTwoPhaseListeners';
|
||||
import accumulateEventTargetListeners from '../accumulateEventTargetListeners';
|
||||
|
||||
import {enableUseEventAPI} from 'shared/ReactFeatureFlags';
|
||||
|
||||
|
||||
+3
@@ -11,6 +11,7 @@
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
describe('BeforeInputEventPlugin', () => {
|
||||
let container;
|
||||
@@ -77,6 +78,8 @@ describe('BeforeInputEventPlugin', () => {
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.enableModernEventSystem = true;
|
||||
React = require('react');
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
|
||||
+2
@@ -470,6 +470,7 @@ describe('ChangeEventPlugin', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.enableModernEventSystem = true;
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
@@ -720,6 +721,7 @@ describe('ChangeEventPlugin', () => {
|
||||
it('mouse enter/leave should be user-blocking but not discrete', async () => {
|
||||
// This is currently behind a feature flag
|
||||
jest.resetModules();
|
||||
ReactFeatureFlags.enableModernEventSystem = true;
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
|
||||
+3
@@ -11,6 +11,7 @@
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
describe('EnterLeaveEventPlugin', () => {
|
||||
let container;
|
||||
@@ -18,6 +19,8 @@ describe('EnterLeaveEventPlugin', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.enableModernEventSystem = true;
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
|
||||
|
||||
+3
@@ -11,11 +11,14 @@
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
describe('SelectEventPlugin', () => {
|
||||
let container;
|
||||
|
||||
beforeEach(() => {
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.enableModernEventSystem = true;
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
|
||||
|
||||
+3
@@ -13,6 +13,7 @@ describe('SimpleEventPlugin', function() {
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let Scheduler;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
let onClick;
|
||||
let container;
|
||||
@@ -36,6 +37,8 @@ describe('SimpleEventPlugin', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
jest.resetModules();
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.enableModernEventSystem = true;
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
Scheduler = require('scheduler');
|
||||
|
||||
Reference in New Issue
Block a user