Skip to content

Commit aa519c1

Browse files
authored
[Flare] Add currentTarget and unify RN and DOM codepaths (facebook#16066)
1 parent 35d2b3b commit aa519c1

File tree

8 files changed

+215
-278
lines changed

8 files changed

+215
-278
lines changed

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

Lines changed: 82 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -372,29 +372,6 @@ const eventResponderContext: ReactDOMResponderContext = {
372372
},
373373
getActiveDocument,
374374
objectAssign: Object.assign,
375-
getEventCurrentTarget(event: ReactDOMResponderEvent): Element {
376-
validateResponderContext();
377-
const target = event.target;
378-
let fiber = getClosestInstanceFromNode(target);
379-
let hostComponent = target;
380-
const currentResponder = ((currentInstance: any): ReactDOMEventComponentInstance)
381-
.responder;
382-
383-
while (fiber !== null) {
384-
const stateNode = fiber.stateNode;
385-
if (
386-
fiber.tag === EventComponent &&
387-
(stateNode === null || stateNode.responder === currentResponder)
388-
) {
389-
break;
390-
}
391-
if (fiber.tag === HostComponent) {
392-
hostComponent = fiber.stateNode;
393-
}
394-
fiber = fiber.return;
395-
}
396-
return ((hostComponent: any): Element);
397-
},
398375
getTimeStamp(): number {
399376
validateResponderContext();
400377
return currentTimeStamp;
@@ -553,6 +530,7 @@ function createDOMResponderEvent(
553530
}
554531

555532
return {
533+
currentTarget: nativeEventTarget,
556534
nativeEvent: nativeEvent,
557535
passive,
558536
passiveSupported,
@@ -650,86 +628,47 @@ function getDOMTargetEventTypesSet(
650628
return cachedSet;
651629
}
652630

653-
function storeTargetEventResponderInstance(
631+
function handleTargetEventResponderInstance(
654632
listeningName: string,
633+
responderEvent: ReactDOMResponderEvent,
655634
eventComponentInstance: ReactDOMEventComponentInstance,
656-
eventResponderInstances: Array<ReactDOMEventComponentInstance>,
657-
eventComponentResponders: null | Set<ReactDOMEventResponder>,
635+
hookComponentResponderValidation: null | Set<ReactDOMEventResponder>,
636+
propagatedEventResponders: null | Set<ReactDOMEventResponder>,
658637
): void {
659638
const responder = eventComponentInstance.responder;
660639
const targetEventTypes = responder.targetEventTypes;
661640
// Validate the target event type exists on the responder
662641
if (targetEventTypes !== undefined) {
663642
const targetEventTypesSet = getDOMTargetEventTypesSet(targetEventTypes);
664643
if (targetEventTypesSet.has(listeningName)) {
665-
eventResponderInstances.push(eventComponentInstance);
666-
if (eventComponentResponders !== null) {
667-
eventComponentResponders.add(responder);
644+
if (hookComponentResponderValidation !== null) {
645+
hookComponentResponderValidation.add(responder);
668646
}
669-
}
670-
}
671-
}
672-
673-
function getTargetEventResponderInstances(
674-
listeningName: string,
675-
targetFiber: null | Fiber,
676-
): Array<ReactDOMEventComponentInstance> {
677-
// We use this to know if we should check add hooks. If there are
678-
// no event targets, then we don't add the hook forms.
679-
const eventComponentResponders = new Set();
680-
const eventResponderInstances = [];
681-
let node = targetFiber;
682-
while (node !== null) {
683-
// Traverse up the fiber tree till we find event component fibers.
684-
const tag = node.tag;
685-
const dependencies = node.dependencies;
686-
if (tag === EventComponent) {
687-
const eventComponentInstance = node.stateNode;
688-
// Switch to the current fiber tree
689-
node = eventComponentInstance.currentFiber;
690-
storeTargetEventResponderInstance(
691-
listeningName,
692-
eventComponentInstance,
693-
eventResponderInstances,
694-
eventComponentResponders,
695-
);
696-
} else if (tag === FunctionComponent && dependencies !== null) {
697-
const events = dependencies.events;
698-
if (events !== null) {
699-
for (let i = 0; i < events.length; i++) {
700-
const eventComponentInstance = events[i];
701-
if (eventComponentResponders.has(eventComponentInstance.responder)) {
702-
storeTargetEventResponderInstance(
703-
listeningName,
704-
eventComponentInstance,
705-
eventResponderInstances,
706-
null,
707-
);
708-
}
647+
const {isHook, props, state} = eventComponentInstance;
648+
const onEvent = responder.onEvent;
649+
if (onEvent !== undefined) {
650+
if (
651+
shouldSkipEventComponent(
652+
eventComponentInstance,
653+
((responder: any): ReactDOMEventResponder),
654+
propagatedEventResponders,
655+
isHook,
656+
)
657+
) {
658+
return;
659+
}
660+
currentInstance = eventComponentInstance;
661+
currentlyInHook = isHook;
662+
onEvent(responderEvent, eventResponderContext, props, state);
663+
if (!isHook) {
664+
checkForLocalPropagationContinuation(
665+
responder,
666+
((propagatedEventResponders: any): Set<ReactDOMEventResponder>),
667+
);
709668
}
710669
}
711670
}
712-
node = node.return;
713-
}
714-
return eventResponderInstances;
715-
}
716-
717-
function getRootEventResponderInstances(
718-
listeningName: string,
719-
): Array<ReactDOMEventComponentInstance> {
720-
const eventResponderInstances = [];
721-
const rootEventInstances = rootEventTypesToEventComponentInstances.get(
722-
listeningName,
723-
);
724-
if (rootEventInstances !== undefined) {
725-
const rootEventComponentInstances = Array.from(rootEventInstances);
726-
727-
for (let i = 0; i < rootEventComponentInstances.length; i++) {
728-
const rootEventComponentInstance = rootEventComponentInstances[i];
729-
eventResponderInstances.push(rootEventComponentInstance);
730-
}
731671
}
732-
return eventResponderInstances;
733672
}
734673

735674
function shouldSkipEventComponent(
@@ -778,10 +717,6 @@ function traverseAndHandleEventResponderInstances(
778717
// - Bubble target phase
779718
// - Root phase
780719

781-
const targetEventResponderInstances = getTargetEventResponderInstances(
782-
listeningName,
783-
targetFiber,
784-
);
785720
const responderEvent = createDOMResponderEvent(
786721
((topLevelType: any): string),
787722
nativeEvent,
@@ -790,67 +725,80 @@ function traverseAndHandleEventResponderInstances(
790725
isPassiveSupported,
791726
);
792727
const propagatedEventResponders: Set<ReactDOMEventResponder> = new Set();
793-
let length = targetEventResponderInstances.length;
794-
let i;
728+
729+
// We use this to know if we should check add hooks. If there are
730+
// no event targets, then we don't add the hook forms.
731+
const hookComponentResponderValidation = new Set();
795732

796733
// Bubbled event phases have the notion of local propagation.
797734
// This means that the propgation chain can be stopped part of the the way
798-
// through processing event component instances. The major difference to other
799-
// events systems is that the stopping of propagation is localized to a single
800-
// phase, rather than both phases.
801-
if (length > 0) {
802-
// Bubble target phase
803-
for (i = 0; i < length; i++) {
804-
const targetEventResponderInstance = targetEventResponderInstances[i];
805-
const {isHook, props, responder, state} = targetEventResponderInstance;
806-
const eventListener = responder.onEvent;
807-
if (eventListener !== undefined) {
808-
if (
809-
shouldSkipEventComponent(
810-
targetEventResponderInstance,
811-
((responder: any): ReactDOMEventResponder),
812-
propagatedEventResponders,
813-
isHook,
814-
)
815-
) {
816-
continue;
817-
}
818-
currentInstance = targetEventResponderInstance;
819-
currentlyInHook = isHook;
820-
eventListener(responderEvent, eventResponderContext, props, state);
821-
if (!isHook) {
822-
checkForLocalPropagationContinuation(
823-
responder,
824-
propagatedEventResponders,
825-
);
735+
// through processing event component instances.
736+
let node = targetFiber;
737+
while (node !== null) {
738+
const {dependencies, stateNode, tag} = node;
739+
if (tag === HostComponent) {
740+
responderEvent.currentTarget = stateNode;
741+
} else if (tag === EventComponent) {
742+
const eventComponentInstance = stateNode;
743+
// Switch to the current fiber tree
744+
node = eventComponentInstance.currentFiber;
745+
handleTargetEventResponderInstance(
746+
listeningName,
747+
responderEvent,
748+
eventComponentInstance,
749+
hookComponentResponderValidation,
750+
propagatedEventResponders,
751+
);
752+
} else if (tag === FunctionComponent && dependencies !== null) {
753+
const events = dependencies.events;
754+
if (events !== null) {
755+
for (let i = 0; i < events.length; i++) {
756+
const eventComponentInstance = events[i];
757+
if (
758+
hookComponentResponderValidation.has(
759+
eventComponentInstance.responder,
760+
)
761+
) {
762+
handleTargetEventResponderInstance(
763+
listeningName,
764+
responderEvent,
765+
eventComponentInstance,
766+
null,
767+
null,
768+
);
769+
}
826770
}
827771
}
828772
}
773+
node = node.return;
829774
}
775+
// Reset currentTarget to be null
776+
responderEvent.currentTarget = null;
830777
// Root phase
831-
const rootEventResponderInstances = getRootEventResponderInstances(
778+
const rootEventInstances = rootEventTypesToEventComponentInstances.get(
832779
listeningName,
833780
);
834-
length = rootEventResponderInstances.length;
835-
if (length > 0) {
836-
for (i = 0; i < length; i++) {
837-
const rootEventResponderInstance = rootEventResponderInstances[i];
838-
const {isHook, props, responder, state} = rootEventResponderInstance;
839-
const eventListener = responder.onRootEvent;
840-
if (eventListener !== undefined) {
781+
if (rootEventInstances !== undefined) {
782+
const rootEventComponentInstances = Array.from(rootEventInstances);
783+
784+
for (let i = 0; i < rootEventComponentInstances.length; i++) {
785+
const rootEventComponentInstance = rootEventComponentInstances[i];
786+
const {isHook, props, responder, state} = rootEventComponentInstance;
787+
const onRootEvent = responder.onRootEvent;
788+
if (onRootEvent !== undefined) {
841789
if (
842790
shouldSkipEventComponent(
843-
rootEventResponderInstance,
791+
rootEventComponentInstance,
844792
responder,
845793
null,
846794
isHook,
847795
)
848796
) {
849797
continue;
850798
}
851-
currentInstance = rootEventResponderInstance;
799+
currentInstance = rootEventComponentInstance;
852800
currentlyInHook = isHook;
853-
eventListener(responderEvent, eventResponderContext, props, state);
801+
onRootEvent(responderEvent, eventResponderContext, props, state);
854802
}
855803
}
856804
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,10 @@ function handleRootPointerEvent(
207207

208208
// Focus should stop being visible if a pointer is used on the element
209209
// after it was focused using a keyboard.
210+
const focusTarget = state.focusTarget;
210211
if (
211-
state.focusTarget === context.getEventCurrentTarget(event) &&
212+
focusTarget !== null &&
213+
context.isTargetWithinNode(event.target, focusTarget) &&
212214
(type === 'mousedown' || type === 'touchstart' || type === 'pointerdown')
213215
) {
214216
dispatchFocusVisibleOutEvent(context, props, state);
@@ -253,7 +255,7 @@ const FocusResponder: ReactDOMEventResponder = {
253255
if (!state.isFocused) {
254256
// Limit focus events to the direct child of the event component.
255257
// Browser focus is not expected to bubble.
256-
state.focusTarget = context.getEventCurrentTarget(event);
258+
state.focusTarget = event.currentTarget;
257259
if (state.focusTarget === target) {
258260
state.isFocused = true;
259261
state.isLocalFocusVisible = isGlobalFocusVisible;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ const HoverResponder: ReactDOMEventResponder = {
321321
if (isEmulatedMouseEvent(event, state)) {
322322
return;
323323
}
324-
state.hoverTarget = context.getEventCurrentTarget(event);
324+
state.hoverTarget = event.currentTarget;
325325
state.ignoreEmulatedMouseEvents = true;
326326
dispatchHoverStartEvents(event, context, props, state);
327327
}

0 commit comments

Comments
 (0)