Skip to content

Commit 32bb44c

Browse files
authored
Clean up modern plugins to remove dead code (facebook#18639)
1 parent 0301f3e commit 32bb44c

14 files changed

+345
-407
lines changed

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

Lines changed: 310 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,12 @@ import {
3333
USE_EVENT_SYSTEM,
3434
} from './EventSystemFlags';
3535

36-
import {HostRoot, HostPortal} from 'react-reconciler/src/ReactWorkTags';
36+
import {
37+
HostRoot,
38+
HostPortal,
39+
ScopeComponent,
40+
HostComponent,
41+
} from 'react-reconciler/src/ReactWorkTags';
3742

3843
import {
3944
addTrappedEventListener,
@@ -86,10 +91,12 @@ import {
8691
import {COMMENT_NODE} from '../shared/HTMLNodeType';
8792
import {topLevelEventsToDispatchConfig} from './DOMEventProperties';
8893
import {batchedEventUpdates} from './ReactDOMUpdateBatching';
94+
import getListener from './getListener';
8995

9096
import {
9197
enableLegacyFBSupport,
9298
enableUseEventAPI,
99+
enableScopeAPI,
93100
} from 'shared/ReactFeatureFlags';
94101
import {
95102
invokeGuardedCallbackAndCatchFirstError,
@@ -153,7 +160,7 @@ const isArray = Array.isArray;
153160
const PossiblyWeakMap = ((typeof WeakMap === 'function' ? WeakMap : Map): any);
154161

155162
// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
156-
export const eventTargetEventListenerStore: WeakMap<
163+
const eventTargetEventListenerStore: WeakMap<
157164
EventTarget,
158165
Map<
159166
DOMTopLevelEventType,
@@ -162,7 +169,7 @@ export const eventTargetEventListenerStore: WeakMap<
162169
> = new PossiblyWeakMap();
163170

164171
// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
165-
export const reactScopeListenerStore: WeakMap<
172+
const reactScopeListenerStore: WeakMap<
166173
ReactScopeMethods,
167174
Map<
168175
DOMTopLevelEventType,
@@ -672,3 +679,303 @@ export function detachListenerFromReactScope(listener: ReactDOMListener): void {
672679
}
673680
}
674681
}
682+
683+
export function accumulateTwoPhaseListeners(
684+
event: ReactSyntheticEvent,
685+
accumulateUseEventListeners?: boolean,
686+
): void {
687+
const phasedRegistrationNames = event.dispatchConfig.phasedRegistrationNames;
688+
const dispatchListeners = [];
689+
const dispatchInstances: Array<Fiber | null> = [];
690+
const dispatchCurrentTargets = [];
691+
692+
const {bubbled, captured} = phasedRegistrationNames;
693+
// If we are not handling EventTarget only phase, then we're doing the
694+
// usual two phase accumulation using the React fiber tree to pick up
695+
// all relevant useEvent and on* prop events.
696+
let instance = event._targetInst;
697+
let lastHostComponent = null;
698+
699+
// Accumulate all instances and listeners via the target -> root path.
700+
while (instance !== null) {
701+
const {stateNode, tag} = instance;
702+
// Handle listeners that are on HostComponents (i.e. <div>)
703+
if (tag === HostComponent && stateNode !== null) {
704+
const currentTarget = stateNode;
705+
lastHostComponent = currentTarget;
706+
// For useEvent listenrs
707+
if (enableUseEventAPI && accumulateUseEventListeners) {
708+
// useEvent event listeners
709+
const targetType = event.type;
710+
const listeners = getListenersFromTarget(currentTarget);
711+
712+
if (listeners !== null) {
713+
const listenersArr = Array.from(listeners);
714+
for (let i = 0; i < listenersArr.length; i++) {
715+
const listener = listenersArr[i];
716+
const {
717+
callback,
718+
event: {capture, type},
719+
} = listener;
720+
if (type === targetType) {
721+
if (capture === true) {
722+
dispatchListeners.unshift(callback);
723+
dispatchInstances.unshift(instance);
724+
dispatchCurrentTargets.unshift(currentTarget);
725+
} else {
726+
dispatchListeners.push(callback);
727+
dispatchInstances.push(instance);
728+
dispatchCurrentTargets.push(currentTarget);
729+
}
730+
}
731+
}
732+
}
733+
}
734+
// Standard React on* listeners, i.e. onClick prop
735+
if (captured !== null) {
736+
const captureListener = getListener(instance, captured);
737+
if (captureListener != null) {
738+
// Capture listeners/instances should go at the start, so we
739+
// unshift them to the start of the array.
740+
dispatchListeners.unshift(captureListener);
741+
dispatchInstances.unshift(instance);
742+
dispatchCurrentTargets.unshift(currentTarget);
743+
}
744+
}
745+
if (bubbled !== null) {
746+
const bubbleListener = getListener(instance, bubbled);
747+
if (bubbleListener != null) {
748+
// Bubble listeners/instances should go at the end, so we
749+
// push them to the end of the array.
750+
dispatchListeners.push(bubbleListener);
751+
dispatchInstances.push(instance);
752+
dispatchCurrentTargets.push(currentTarget);
753+
}
754+
}
755+
}
756+
if (
757+
enableUseEventAPI &&
758+
enableScopeAPI &&
759+
accumulateUseEventListeners &&
760+
tag === ScopeComponent &&
761+
lastHostComponent !== null
762+
) {
763+
const reactScope = stateNode.methods;
764+
const eventTypeMap = reactScopeListenerStore.get(reactScope);
765+
if (eventTypeMap !== undefined) {
766+
const type = ((event.type: any): DOMTopLevelEventType);
767+
const listeners = eventTypeMap.get(type);
768+
if (listeners !== undefined) {
769+
const captureListeners = Array.from(listeners.captured);
770+
const bubbleListeners = Array.from(listeners.bubbled);
771+
const lastCurrentTarget = ((lastHostComponent: any): Element);
772+
773+
for (let i = 0; i < captureListeners.length; i++) {
774+
const listener = captureListeners[i];
775+
const {callback} = listener;
776+
dispatchListeners.unshift(callback);
777+
dispatchInstances.unshift(instance);
778+
dispatchCurrentTargets.unshift(lastCurrentTarget);
779+
}
780+
for (let i = 0; i < bubbleListeners.length; i++) {
781+
const listener = bubbleListeners[i];
782+
const {callback} = listener;
783+
dispatchListeners.push(callback);
784+
dispatchInstances.push(instance);
785+
dispatchCurrentTargets.push(lastCurrentTarget);
786+
}
787+
}
788+
}
789+
}
790+
instance = instance.return;
791+
}
792+
793+
// To prevent allocation to the event unless we actually
794+
// have listeners we check the length of one of the arrays.
795+
if (dispatchListeners.length > 0) {
796+
event._dispatchListeners = dispatchListeners;
797+
event._dispatchInstances = dispatchInstances;
798+
event._dispatchCurrentTargets = dispatchCurrentTargets;
799+
}
800+
}
801+
802+
export function accumulateEventTargetListeners(
803+
event: ReactSyntheticEvent,
804+
currentTarget: EventTarget,
805+
): void {
806+
const dispatchListeners = [];
807+
const dispatchInstances: Array<Fiber | null> = [];
808+
const dispatchCurrentTargets = [];
809+
810+
const eventTypeMap = eventTargetEventListenerStore.get(currentTarget);
811+
if (eventTypeMap !== undefined) {
812+
const type = ((event.type: any): DOMTopLevelEventType);
813+
const listeners = eventTypeMap.get(type);
814+
if (listeners !== undefined) {
815+
const isCapturePhase = (event: any).eventPhase === 1;
816+
817+
if (isCapturePhase) {
818+
const captureListeners = Array.from(listeners.captured);
819+
820+
for (let i = captureListeners.length - 1; i >= 0; i--) {
821+
const listener = captureListeners[i];
822+
const {callback} = listener;
823+
dispatchListeners.push(callback);
824+
// EventTarget listeners do not have instances, as there
825+
// is no backing Fiber instance for them (window, document etc).
826+
dispatchInstances.push(null);
827+
dispatchCurrentTargets.push(currentTarget);
828+
}
829+
} else {
830+
const bubbleListeners = Array.from(listeners.bubbled);
831+
832+
for (let i = 0; i < bubbleListeners.length; i++) {
833+
const listener = bubbleListeners[i];
834+
const {callback} = listener;
835+
dispatchListeners.push(callback);
836+
// EventTarget listeners do not have instances, as there
837+
// is no backing Fiber instance for them (window, document etc).
838+
dispatchInstances.push(null);
839+
dispatchCurrentTargets.push(currentTarget);
840+
}
841+
}
842+
}
843+
}
844+
// To prevent allocation to the event unless we actually
845+
// have listeners we check the length of one of the arrays.
846+
if (dispatchListeners.length > 0) {
847+
event._dispatchListeners = dispatchListeners;
848+
event._dispatchInstances = dispatchInstances;
849+
event._dispatchCurrentTargets = dispatchCurrentTargets;
850+
}
851+
}
852+
853+
function getParent(inst: Fiber | null): Fiber | null {
854+
if (inst === null) {
855+
return null;
856+
}
857+
do {
858+
inst = inst.return;
859+
// TODO: If this is a HostRoot we might want to bail out.
860+
// That is depending on if we want nested subtrees (layers) to bubble
861+
// events to their parent. We could also go through parentNode on the
862+
// host node but that wouldn't work for React Native and doesn't let us
863+
// do the portal feature.
864+
} while (inst && inst.tag !== HostComponent);
865+
if (inst) {
866+
return inst;
867+
}
868+
return null;
869+
}
870+
871+
/**
872+
* Return the lowest common ancestor of A and B, or null if they are in
873+
* different trees.
874+
*/
875+
function getLowestCommonAncestor(instA: Fiber, instB: Fiber): Fiber | null {
876+
let nodeA = instA;
877+
let nodeB = instB;
878+
let depthA = 0;
879+
for (let tempA = nodeA; tempA; tempA = getParent(tempA)) {
880+
depthA++;
881+
}
882+
let depthB = 0;
883+
for (let tempB = nodeB; tempB; tempB = getParent(tempB)) {
884+
depthB++;
885+
}
886+
887+
// If A is deeper, crawl up.
888+
while (depthA - depthB > 0) {
889+
nodeA = getParent(nodeA);
890+
depthA--;
891+
}
892+
893+
// If B is deeper, crawl up.
894+
while (depthB - depthA > 0) {
895+
nodeB = getParent(nodeB);
896+
depthB--;
897+
}
898+
899+
// Walk in lockstep until we find a match.
900+
let depth = depthA;
901+
while (depth--) {
902+
if (nodeA === nodeB || (nodeB !== null && nodeA === nodeB.alternate)) {
903+
return nodeA;
904+
}
905+
nodeA = getParent(nodeA);
906+
nodeB = getParent(nodeB);
907+
}
908+
return null;
909+
}
910+
911+
function accumulateEnterLeaveListenersForEvent(
912+
event: ReactSyntheticEvent,
913+
target: Fiber,
914+
common: Fiber | null,
915+
capture: boolean,
916+
): void {
917+
const registrationName = event.dispatchConfig.registrationName;
918+
if (registrationName === undefined) {
919+
return;
920+
}
921+
const dispatchListeners = [];
922+
const dispatchInstances: Array<Fiber | null> = [];
923+
const dispatchCurrentTargets = [];
924+
925+
let instance = target;
926+
while (instance !== null) {
927+
if (instance === common) {
928+
break;
929+
}
930+
const {alternate, stateNode, tag} = instance;
931+
if (alternate !== null && alternate === common) {
932+
break;
933+
}
934+
if (tag === HostComponent && stateNode !== null) {
935+
const currentTarget = stateNode;
936+
if (capture) {
937+
const captureListener = getListener(instance, registrationName);
938+
if (captureListener != null) {
939+
// Capture listeners/instances should go at the start, so we
940+
// unshift them to the start of the array.
941+
dispatchListeners.unshift(captureListener);
942+
dispatchInstances.unshift(instance);
943+
dispatchCurrentTargets.unshift(currentTarget);
944+
}
945+
} else {
946+
const bubbleListener = getListener(instance, registrationName);
947+
if (bubbleListener != null) {
948+
// Bubble listeners/instances should go at the end, so we
949+
// push them to the end of the array.
950+
dispatchListeners.push(bubbleListener);
951+
dispatchInstances.push(instance);
952+
dispatchCurrentTargets.push(currentTarget);
953+
}
954+
}
955+
}
956+
instance = instance.return;
957+
}
958+
// To prevent allocation to the event unless we actually
959+
// have listeners we check the length of one of the arrays.
960+
if (dispatchListeners.length > 0) {
961+
event._dispatchListeners = dispatchListeners;
962+
event._dispatchInstances = dispatchInstances;
963+
event._dispatchCurrentTargets = dispatchCurrentTargets;
964+
}
965+
}
966+
967+
export function accumulateEnterLeaveListeners(
968+
leaveEvent: ReactSyntheticEvent,
969+
enterEvent: ReactSyntheticEvent,
970+
from: Fiber | null,
971+
to: Fiber | null,
972+
): void {
973+
const common = from && to ? getLowestCommonAncestor(from, to) : null;
974+
975+
if (from !== null) {
976+
accumulateEnterLeaveListenersForEvent(leaveEvent, from, common, false);
977+
}
978+
if (to !== null) {
979+
accumulateEnterLeaveListenersForEvent(enterEvent, to, common, true);
980+
}
981+
}

0 commit comments

Comments
 (0)