mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[Flare] Add currentTarget and unify RN and DOM codepaths (#16066)
This commit is contained in:
+82
-134
@@ -372,29 +372,6 @@ const eventResponderContext: ReactDOMResponderContext = {
|
||||
},
|
||||
getActiveDocument,
|
||||
objectAssign: Object.assign,
|
||||
getEventCurrentTarget(event: ReactDOMResponderEvent): Element {
|
||||
validateResponderContext();
|
||||
const target = event.target;
|
||||
let fiber = getClosestInstanceFromNode(target);
|
||||
let hostComponent = target;
|
||||
const currentResponder = ((currentInstance: any): ReactDOMEventComponentInstance)
|
||||
.responder;
|
||||
|
||||
while (fiber !== null) {
|
||||
const stateNode = fiber.stateNode;
|
||||
if (
|
||||
fiber.tag === EventComponent &&
|
||||
(stateNode === null || stateNode.responder === currentResponder)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
if (fiber.tag === HostComponent) {
|
||||
hostComponent = fiber.stateNode;
|
||||
}
|
||||
fiber = fiber.return;
|
||||
}
|
||||
return ((hostComponent: any): Element);
|
||||
},
|
||||
getTimeStamp(): number {
|
||||
validateResponderContext();
|
||||
return currentTimeStamp;
|
||||
@@ -553,6 +530,7 @@ function createDOMResponderEvent(
|
||||
}
|
||||
|
||||
return {
|
||||
currentTarget: nativeEventTarget,
|
||||
nativeEvent: nativeEvent,
|
||||
passive,
|
||||
passiveSupported,
|
||||
@@ -650,11 +628,12 @@ function getDOMTargetEventTypesSet(
|
||||
return cachedSet;
|
||||
}
|
||||
|
||||
function storeTargetEventResponderInstance(
|
||||
function handleTargetEventResponderInstance(
|
||||
listeningName: string,
|
||||
responderEvent: ReactDOMResponderEvent,
|
||||
eventComponentInstance: ReactDOMEventComponentInstance,
|
||||
eventResponderInstances: Array<ReactDOMEventComponentInstance>,
|
||||
eventComponentResponders: null | Set<ReactDOMEventResponder>,
|
||||
hookComponentResponderValidation: null | Set<ReactDOMEventResponder>,
|
||||
propagatedEventResponders: null | Set<ReactDOMEventResponder>,
|
||||
): void {
|
||||
const responder = eventComponentInstance.responder;
|
||||
const targetEventTypes = responder.targetEventTypes;
|
||||
@@ -662,74 +641,34 @@ function storeTargetEventResponderInstance(
|
||||
if (targetEventTypes !== undefined) {
|
||||
const targetEventTypesSet = getDOMTargetEventTypesSet(targetEventTypes);
|
||||
if (targetEventTypesSet.has(listeningName)) {
|
||||
eventResponderInstances.push(eventComponentInstance);
|
||||
if (eventComponentResponders !== null) {
|
||||
eventComponentResponders.add(responder);
|
||||
if (hookComponentResponderValidation !== null) {
|
||||
hookComponentResponderValidation.add(responder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTargetEventResponderInstances(
|
||||
listeningName: string,
|
||||
targetFiber: null | Fiber,
|
||||
): Array<ReactDOMEventComponentInstance> {
|
||||
// We use this to know if we should check add hooks. If there are
|
||||
// no event targets, then we don't add the hook forms.
|
||||
const eventComponentResponders = new Set();
|
||||
const eventResponderInstances = [];
|
||||
let node = targetFiber;
|
||||
while (node !== null) {
|
||||
// Traverse up the fiber tree till we find event component fibers.
|
||||
const tag = node.tag;
|
||||
const dependencies = node.dependencies;
|
||||
if (tag === EventComponent) {
|
||||
const eventComponentInstance = node.stateNode;
|
||||
// Switch to the current fiber tree
|
||||
node = eventComponentInstance.currentFiber;
|
||||
storeTargetEventResponderInstance(
|
||||
listeningName,
|
||||
eventComponentInstance,
|
||||
eventResponderInstances,
|
||||
eventComponentResponders,
|
||||
);
|
||||
} else if (tag === FunctionComponent && dependencies !== null) {
|
||||
const events = dependencies.events;
|
||||
if (events !== null) {
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
const eventComponentInstance = events[i];
|
||||
if (eventComponentResponders.has(eventComponentInstance.responder)) {
|
||||
storeTargetEventResponderInstance(
|
||||
listeningName,
|
||||
eventComponentInstance,
|
||||
eventResponderInstances,
|
||||
null,
|
||||
);
|
||||
}
|
||||
const {isHook, props, state} = eventComponentInstance;
|
||||
const onEvent = responder.onEvent;
|
||||
if (onEvent !== undefined) {
|
||||
if (
|
||||
shouldSkipEventComponent(
|
||||
eventComponentInstance,
|
||||
((responder: any): ReactDOMEventResponder),
|
||||
propagatedEventResponders,
|
||||
isHook,
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
currentInstance = eventComponentInstance;
|
||||
currentlyInHook = isHook;
|
||||
onEvent(responderEvent, eventResponderContext, props, state);
|
||||
if (!isHook) {
|
||||
checkForLocalPropagationContinuation(
|
||||
responder,
|
||||
((propagatedEventResponders: any): Set<ReactDOMEventResponder>),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
node = node.return;
|
||||
}
|
||||
return eventResponderInstances;
|
||||
}
|
||||
|
||||
function getRootEventResponderInstances(
|
||||
listeningName: string,
|
||||
): Array<ReactDOMEventComponentInstance> {
|
||||
const eventResponderInstances = [];
|
||||
const rootEventInstances = rootEventTypesToEventComponentInstances.get(
|
||||
listeningName,
|
||||
);
|
||||
if (rootEventInstances !== undefined) {
|
||||
const rootEventComponentInstances = Array.from(rootEventInstances);
|
||||
|
||||
for (let i = 0; i < rootEventComponentInstances.length; i++) {
|
||||
const rootEventComponentInstance = rootEventComponentInstances[i];
|
||||
eventResponderInstances.push(rootEventComponentInstance);
|
||||
}
|
||||
}
|
||||
return eventResponderInstances;
|
||||
}
|
||||
|
||||
function shouldSkipEventComponent(
|
||||
@@ -778,10 +717,6 @@ function traverseAndHandleEventResponderInstances(
|
||||
// - Bubble target phase
|
||||
// - Root phase
|
||||
|
||||
const targetEventResponderInstances = getTargetEventResponderInstances(
|
||||
listeningName,
|
||||
targetFiber,
|
||||
);
|
||||
const responderEvent = createDOMResponderEvent(
|
||||
((topLevelType: any): string),
|
||||
nativeEvent,
|
||||
@@ -790,57 +725,70 @@ function traverseAndHandleEventResponderInstances(
|
||||
isPassiveSupported,
|
||||
);
|
||||
const propagatedEventResponders: Set<ReactDOMEventResponder> = new Set();
|
||||
let length = targetEventResponderInstances.length;
|
||||
let i;
|
||||
|
||||
// We use this to know if we should check add hooks. If there are
|
||||
// no event targets, then we don't add the hook forms.
|
||||
const hookComponentResponderValidation = new Set();
|
||||
|
||||
// Bubbled event phases have the notion of local propagation.
|
||||
// This means that the propgation chain can be stopped part of the the way
|
||||
// through processing event component instances. The major difference to other
|
||||
// events systems is that the stopping of propagation is localized to a single
|
||||
// phase, rather than both phases.
|
||||
if (length > 0) {
|
||||
// Bubble target phase
|
||||
for (i = 0; i < length; i++) {
|
||||
const targetEventResponderInstance = targetEventResponderInstances[i];
|
||||
const {isHook, props, responder, state} = targetEventResponderInstance;
|
||||
const eventListener = responder.onEvent;
|
||||
if (eventListener !== undefined) {
|
||||
if (
|
||||
shouldSkipEventComponent(
|
||||
targetEventResponderInstance,
|
||||
((responder: any): ReactDOMEventResponder),
|
||||
propagatedEventResponders,
|
||||
isHook,
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
currentInstance = targetEventResponderInstance;
|
||||
currentlyInHook = isHook;
|
||||
eventListener(responderEvent, eventResponderContext, props, state);
|
||||
if (!isHook) {
|
||||
checkForLocalPropagationContinuation(
|
||||
responder,
|
||||
propagatedEventResponders,
|
||||
);
|
||||
// through processing event component instances.
|
||||
let node = targetFiber;
|
||||
while (node !== null) {
|
||||
const {dependencies, stateNode, tag} = node;
|
||||
if (tag === HostComponent) {
|
||||
responderEvent.currentTarget = stateNode;
|
||||
} else if (tag === EventComponent) {
|
||||
const eventComponentInstance = stateNode;
|
||||
// Switch to the current fiber tree
|
||||
node = eventComponentInstance.currentFiber;
|
||||
handleTargetEventResponderInstance(
|
||||
listeningName,
|
||||
responderEvent,
|
||||
eventComponentInstance,
|
||||
hookComponentResponderValidation,
|
||||
propagatedEventResponders,
|
||||
);
|
||||
} else if (tag === FunctionComponent && dependencies !== null) {
|
||||
const events = dependencies.events;
|
||||
if (events !== null) {
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
const eventComponentInstance = events[i];
|
||||
if (
|
||||
hookComponentResponderValidation.has(
|
||||
eventComponentInstance.responder,
|
||||
)
|
||||
) {
|
||||
handleTargetEventResponderInstance(
|
||||
listeningName,
|
||||
responderEvent,
|
||||
eventComponentInstance,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node = node.return;
|
||||
}
|
||||
// Reset currentTarget to be null
|
||||
responderEvent.currentTarget = null;
|
||||
// Root phase
|
||||
const rootEventResponderInstances = getRootEventResponderInstances(
|
||||
const rootEventInstances = rootEventTypesToEventComponentInstances.get(
|
||||
listeningName,
|
||||
);
|
||||
length = rootEventResponderInstances.length;
|
||||
if (length > 0) {
|
||||
for (i = 0; i < length; i++) {
|
||||
const rootEventResponderInstance = rootEventResponderInstances[i];
|
||||
const {isHook, props, responder, state} = rootEventResponderInstance;
|
||||
const eventListener = responder.onRootEvent;
|
||||
if (eventListener !== undefined) {
|
||||
if (rootEventInstances !== undefined) {
|
||||
const rootEventComponentInstances = Array.from(rootEventInstances);
|
||||
|
||||
for (let i = 0; i < rootEventComponentInstances.length; i++) {
|
||||
const rootEventComponentInstance = rootEventComponentInstances[i];
|
||||
const {isHook, props, responder, state} = rootEventComponentInstance;
|
||||
const onRootEvent = responder.onRootEvent;
|
||||
if (onRootEvent !== undefined) {
|
||||
if (
|
||||
shouldSkipEventComponent(
|
||||
rootEventResponderInstance,
|
||||
rootEventComponentInstance,
|
||||
responder,
|
||||
null,
|
||||
isHook,
|
||||
@@ -848,9 +796,9 @@ function traverseAndHandleEventResponderInstances(
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
currentInstance = rootEventResponderInstance;
|
||||
currentInstance = rootEventComponentInstance;
|
||||
currentlyInHook = isHook;
|
||||
eventListener(responderEvent, eventResponderContext, props, state);
|
||||
onRootEvent(responderEvent, eventResponderContext, props, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -207,8 +207,10 @@ function handleRootPointerEvent(
|
||||
|
||||
// Focus should stop being visible if a pointer is used on the element
|
||||
// after it was focused using a keyboard.
|
||||
const focusTarget = state.focusTarget;
|
||||
if (
|
||||
state.focusTarget === context.getEventCurrentTarget(event) &&
|
||||
focusTarget !== null &&
|
||||
context.isTargetWithinNode(event.target, focusTarget) &&
|
||||
(type === 'mousedown' || type === 'touchstart' || type === 'pointerdown')
|
||||
) {
|
||||
dispatchFocusVisibleOutEvent(context, props, state);
|
||||
@@ -253,7 +255,7 @@ const FocusResponder: ReactDOMEventResponder = {
|
||||
if (!state.isFocused) {
|
||||
// Limit focus events to the direct child of the event component.
|
||||
// Browser focus is not expected to bubble.
|
||||
state.focusTarget = context.getEventCurrentTarget(event);
|
||||
state.focusTarget = event.currentTarget;
|
||||
if (state.focusTarget === target) {
|
||||
state.isFocused = true;
|
||||
state.isLocalFocusVisible = isGlobalFocusVisible;
|
||||
|
||||
+1
-1
@@ -321,7 +321,7 @@ const HoverResponder: ReactDOMEventResponder = {
|
||||
if (isEmulatedMouseEvent(event, state)) {
|
||||
return;
|
||||
}
|
||||
state.hoverTarget = context.getEventCurrentTarget(event);
|
||||
state.hoverTarget = event.currentTarget;
|
||||
state.ignoreEmulatedMouseEvents = true;
|
||||
dispatchHoverStartEvents(event, context, props, state);
|
||||
}
|
||||
|
||||
+24
-13
@@ -57,7 +57,7 @@ type PressState = {
|
||||
isPressWithinResponderRegion: boolean,
|
||||
longPressTimeout: null | number,
|
||||
pointerType: PointerType,
|
||||
pressTarget: null | Element,
|
||||
pressTarget: null | Element | Document,
|
||||
pressEndTimeout: null | number,
|
||||
pressStartTimeout: null | number,
|
||||
responderRegionOnActivation: null | $ReadOnly<{|
|
||||
@@ -619,6 +619,11 @@ function handleStopPropagation(
|
||||
}
|
||||
}
|
||||
|
||||
function targetIsDocument(target: null | Node): boolean {
|
||||
// When target is null, it is the root
|
||||
return target === null || target.nodeType === 9;
|
||||
}
|
||||
|
||||
const PressResponder: ReactDOMEventResponder = {
|
||||
displayName: 'Press',
|
||||
targetEventTypes,
|
||||
@@ -691,7 +696,7 @@ const PressResponder: ReactDOMEventResponder = {
|
||||
// We set these here, before the button check so we have this
|
||||
// data around for handling of the context menu
|
||||
state.pointerType = pointerType;
|
||||
state.pressTarget = context.getEventCurrentTarget(event);
|
||||
const pressTarget = (state.pressTarget = event.currentTarget);
|
||||
if (isPointerEvent) {
|
||||
state.activePointerId = pointerId;
|
||||
} else if (isTouchEvent) {
|
||||
@@ -712,12 +717,14 @@ const PressResponder: ReactDOMEventResponder = {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.responderRegionOnActivation = calculateResponderRegion(
|
||||
context,
|
||||
state.pressTarget,
|
||||
props,
|
||||
);
|
||||
// Exclude document targets
|
||||
if (!targetIsDocument(pressTarget)) {
|
||||
state.responderRegionOnActivation = calculateResponderRegion(
|
||||
context,
|
||||
((pressTarget: any): Element),
|
||||
props,
|
||||
);
|
||||
}
|
||||
state.responderRegionOnDeactivation = null;
|
||||
state.isPressWithinResponderRegion = true;
|
||||
dispatchPressStartEvents(event, context, props, state);
|
||||
@@ -824,11 +831,13 @@ const PressResponder: ReactDOMEventResponder = {
|
||||
}
|
||||
state.touchEvent = touchEvent;
|
||||
}
|
||||
const pressTarget = state.pressTarget;
|
||||
|
||||
if (
|
||||
state.pressTarget !== null &&
|
||||
pressTarget !== null &&
|
||||
!targetIsDocument(pressTarget) &&
|
||||
(pointerType !== 'mouse' ||
|
||||
!context.isTargetWithinNode(target, state.pressTarget))
|
||||
!context.isTargetWithinNode(target, pressTarget))
|
||||
) {
|
||||
// Calculate the responder region we use for deactivation, as the
|
||||
// element dimensions may have changed since activation.
|
||||
@@ -934,14 +943,16 @@ const PressResponder: ReactDOMEventResponder = {
|
||||
}
|
||||
|
||||
const wasLongPressed = state.isLongPressed;
|
||||
const pressTarget = state.pressTarget;
|
||||
dispatchPressEndEvents(event, context, props, state);
|
||||
|
||||
if (state.pressTarget !== null && props.onPress) {
|
||||
if (pressTarget !== null && props.onPress) {
|
||||
if (
|
||||
!isKeyboardEvent &&
|
||||
state.pressTarget !== null &&
|
||||
pressTarget !== null &&
|
||||
!targetIsDocument(pressTarget) &&
|
||||
(pointerType !== 'mouse' ||
|
||||
!context.isTargetWithinNode(target, state.pressTarget))
|
||||
!context.isTargetWithinNode(target, pressTarget))
|
||||
) {
|
||||
// If the event target isn't within the press target, check if we're still
|
||||
// within the responder region. The region may have changed if the
|
||||
|
||||
+1
-3
@@ -537,9 +537,7 @@ const PressResponder: ReactNativeEventResponder = {
|
||||
if (type === 'topTouchStart') {
|
||||
if (!state.isPressed) {
|
||||
state.pointerType = 'touch';
|
||||
const pressTarget = (state.pressTarget = context.getEventCurrentTarget(
|
||||
event,
|
||||
));
|
||||
const pressTarget = (state.pressTarget = event.currentTarget);
|
||||
|
||||
const touchEvent = getTouchFromPressEvent(nativeEvent);
|
||||
if (touchEvent === null) {
|
||||
|
||||
+101
-121
@@ -7,7 +7,11 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import {EventComponent, HostComponent} from 'shared/ReactWorkTags';
|
||||
import {
|
||||
EventComponent,
|
||||
HostComponent,
|
||||
FunctionComponent,
|
||||
} from 'shared/ReactWorkTags';
|
||||
import type {Fiber} from 'react-reconciler/src/ReactFiber';
|
||||
import {
|
||||
batchedEventUpdates,
|
||||
@@ -192,31 +196,6 @@ const eventResponderContext: ReactNativeResponderContext = {
|
||||
}
|
||||
return false;
|
||||
},
|
||||
getEventCurrentTarget(
|
||||
event: ReactNativeResponderEvent,
|
||||
): ReactNativeEventTarget {
|
||||
validateResponderContext();
|
||||
const target = event.target;
|
||||
let fiber = getFiberFromTarget(target);
|
||||
let hostComponent = target;
|
||||
const currentResponder = ((currentInstance: any): ReactNativeEventComponentInstance)
|
||||
.responder;
|
||||
|
||||
while (fiber !== null) {
|
||||
const stateNode = fiber.stateNode;
|
||||
if (
|
||||
fiber.tag === EventComponent &&
|
||||
(stateNode === null || stateNode.responder === currentResponder)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
if (fiber.tag === HostComponent) {
|
||||
hostComponent = fiber.stateNode;
|
||||
}
|
||||
fiber = fiber.return;
|
||||
}
|
||||
return ((hostComponent: any): ReactNativeEventTarget);
|
||||
},
|
||||
getTargetBoundingRect(
|
||||
target: ReactNativeEventTarget,
|
||||
callback: ({
|
||||
@@ -320,7 +299,7 @@ function getFiberFromTarget(
|
||||
if (target === null) {
|
||||
return null;
|
||||
}
|
||||
return ((target.canonical._internalInstanceHandle: any): Fiber);
|
||||
return ((target.canonical._internalInstanceHandle: any): Fiber) || null;
|
||||
}
|
||||
|
||||
function processTimers(
|
||||
@@ -355,15 +334,12 @@ function createFabricResponderEvent(
|
||||
nativeEvent: ReactFaricEvent,
|
||||
target: null | ReactNativeEventTarget,
|
||||
): ReactNativeResponderEvent {
|
||||
const responderEvent = {
|
||||
return {
|
||||
currentTarget: target,
|
||||
nativeEvent,
|
||||
target,
|
||||
type: topLevelType,
|
||||
};
|
||||
if (__DEV__) {
|
||||
Object.freeze(responderEvent);
|
||||
}
|
||||
return responderEvent;
|
||||
}
|
||||
|
||||
function validateResponderContext(): void {
|
||||
@@ -490,35 +466,6 @@ function getFabricTargetEventTypesSet(
|
||||
return cachedSet;
|
||||
}
|
||||
|
||||
// TODO this function is almost an exact copy of the DOM version, we should
|
||||
// somehow share the logic
|
||||
function getTargetEventResponderInstances(
|
||||
topLevelType: ReactNativeEventResponderEventType,
|
||||
targetFiber: null | Fiber,
|
||||
): Array<ReactNativeEventComponentInstance> {
|
||||
const eventResponderInstances = [];
|
||||
let node = targetFiber;
|
||||
while (node !== null) {
|
||||
// Traverse up the fiber tree till we find event component fibers.
|
||||
if (node.tag === EventComponent) {
|
||||
const eventComponentInstance = node.stateNode;
|
||||
const responder = eventComponentInstance.responder;
|
||||
const targetEventTypes = responder.targetEventTypes;
|
||||
// Validate the target event type exists on the responder
|
||||
if (targetEventTypes !== undefined) {
|
||||
const targetEventTypesSet = getFabricTargetEventTypesSet(
|
||||
targetEventTypes,
|
||||
);
|
||||
if (targetEventTypesSet.has(topLevelType)) {
|
||||
eventResponderInstances.push(eventComponentInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
node = node.return;
|
||||
}
|
||||
return eventResponderInstances;
|
||||
}
|
||||
|
||||
// TODO this function is almost an exact copy of the DOM version, we should
|
||||
// somehow share the logic
|
||||
function shouldSkipEventComponent(
|
||||
@@ -551,22 +498,47 @@ function checkForLocalPropagationContinuation(
|
||||
|
||||
// TODO this function is almost an exact copy of the DOM version, we should
|
||||
// somehow share the logic
|
||||
function getRootEventResponderInstances(
|
||||
topLevelType: string,
|
||||
): Array<ReactNativeEventComponentInstance> {
|
||||
const eventResponderInstances = [];
|
||||
const rootEventInstances = rootEventTypesToEventComponentInstances.get(
|
||||
topLevelType,
|
||||
);
|
||||
if (rootEventInstances !== undefined) {
|
||||
const rootEventComponentInstances = Array.from(rootEventInstances);
|
||||
|
||||
for (let i = 0; i < rootEventComponentInstances.length; i++) {
|
||||
const rootEventComponentInstance = rootEventComponentInstances[i];
|
||||
eventResponderInstances.push(rootEventComponentInstance);
|
||||
function handleTargetEventResponderInstance(
|
||||
topLevelType: ReactNativeEventResponderEventType,
|
||||
responderEvent: ReactNativeResponderEvent,
|
||||
eventComponentInstance: ReactNativeEventComponentInstance,
|
||||
hookComponentResponderValidation: null | Set<ReactNativeEventResponder>,
|
||||
propagatedEventResponders: null | Set<ReactNativeEventResponder>,
|
||||
): void {
|
||||
const responder = eventComponentInstance.responder;
|
||||
const targetEventTypes = responder.targetEventTypes;
|
||||
// Validate the target event type exists on the responder
|
||||
if (targetEventTypes !== undefined) {
|
||||
const targetEventTypesSet = getFabricTargetEventTypesSet(targetEventTypes);
|
||||
if (targetEventTypesSet.has(topLevelType)) {
|
||||
if (hookComponentResponderValidation !== null) {
|
||||
hookComponentResponderValidation.add(responder);
|
||||
}
|
||||
const {isHook, props, state} = eventComponentInstance;
|
||||
const eventListener = responder.onEvent;
|
||||
if (eventListener !== undefined) {
|
||||
if (
|
||||
shouldSkipEventComponent(
|
||||
eventComponentInstance,
|
||||
((responder: any): ReactNativeEventResponder),
|
||||
propagatedEventResponders,
|
||||
isHook,
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
currentInstance = eventComponentInstance;
|
||||
currentlyInHook = isHook;
|
||||
eventListener(responderEvent, eventResponderContext, props, state);
|
||||
if (!isHook) {
|
||||
checkForLocalPropagationContinuation(
|
||||
responder,
|
||||
((propagatedEventResponders: any): Set<ReactNativeEventResponder>),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return eventResponderInstances;
|
||||
}
|
||||
|
||||
// TODO this function is almost an exact copy of the DOM version, we should
|
||||
@@ -580,10 +552,6 @@ function traverseAndHandleEventResponderInstances(
|
||||
// - Bubble target phase
|
||||
// - Root phase
|
||||
|
||||
const targetEventResponderInstances = getTargetEventResponderInstances(
|
||||
topLevelType,
|
||||
targetFiber,
|
||||
);
|
||||
const responderEvent = createFabricResponderEvent(
|
||||
topLevelType,
|
||||
nativeEvent,
|
||||
@@ -592,58 +560,70 @@ function traverseAndHandleEventResponderInstances(
|
||||
: null,
|
||||
);
|
||||
const propagatedEventResponders: Set<ReactNativeEventResponder> = new Set();
|
||||
let length = targetEventResponderInstances.length;
|
||||
let i;
|
||||
|
||||
// We use this to know if we should check add hooks. If there are
|
||||
// no event targets, then we don't add the hook forms.
|
||||
const hookComponentResponderValidation = new Set();
|
||||
|
||||
// Bubbled event phases have the notion of local propagation.
|
||||
// This means that the propgation chain can be stopped part of the the way
|
||||
// through processing event component instances. The major difference to other
|
||||
// events systems is that the stopping of propagation is localized to a single
|
||||
// phase, rather than both phases.
|
||||
if (length > 0) {
|
||||
// Bubble target phase
|
||||
for (i = 0; i < length; i++) {
|
||||
const targetEventResponderInstance = targetEventResponderInstances[i];
|
||||
const {isHook, responder, props, state} = targetEventResponderInstance;
|
||||
const eventListener = ((responder: any): ReactNativeEventResponder)
|
||||
.onEvent;
|
||||
if (eventListener !== undefined) {
|
||||
if (
|
||||
shouldSkipEventComponent(
|
||||
targetEventResponderInstance,
|
||||
((responder: any): ReactNativeEventResponder),
|
||||
propagatedEventResponders,
|
||||
isHook,
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
currentInstance = targetEventResponderInstance;
|
||||
currentlyInHook = isHook;
|
||||
eventListener(responderEvent, eventResponderContext, props, state);
|
||||
if (!isHook) {
|
||||
checkForLocalPropagationContinuation(
|
||||
((responder: any): ReactNativeEventResponder),
|
||||
propagatedEventResponders,
|
||||
);
|
||||
// through processing event component instances.
|
||||
let node = targetFiber;
|
||||
while (node !== null) {
|
||||
const {dependencies, stateNode, tag} = node;
|
||||
if (tag === HostComponent) {
|
||||
responderEvent.currentTarget = stateNode;
|
||||
} else if (tag === EventComponent) {
|
||||
const eventComponentInstance = stateNode;
|
||||
// Switch to the current fiber tree
|
||||
node = eventComponentInstance.currentFiber;
|
||||
handleTargetEventResponderInstance(
|
||||
topLevelType,
|
||||
responderEvent,
|
||||
eventComponentInstance,
|
||||
hookComponentResponderValidation,
|
||||
propagatedEventResponders,
|
||||
);
|
||||
} else if (tag === FunctionComponent && dependencies !== null) {
|
||||
const events = dependencies.events;
|
||||
if (events !== null) {
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
const eventComponentInstance = events[i];
|
||||
if (
|
||||
hookComponentResponderValidation.has(
|
||||
eventComponentInstance.responder,
|
||||
)
|
||||
) {
|
||||
handleTargetEventResponderInstance(
|
||||
topLevelType,
|
||||
responderEvent,
|
||||
eventComponentInstance,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node = node.return;
|
||||
}
|
||||
// Reset currentTarget to be null
|
||||
responderEvent.currentTarget = null;
|
||||
// Root phase
|
||||
const rootEventResponderInstances = getRootEventResponderInstances(
|
||||
const rootEventInstances = rootEventTypesToEventComponentInstances.get(
|
||||
topLevelType,
|
||||
);
|
||||
length = rootEventResponderInstances.length;
|
||||
if (length > 0) {
|
||||
for (i = 0; i < length; i++) {
|
||||
const rootEventResponderInstance = rootEventResponderInstances[i];
|
||||
const {isHook, props, responder, state} = rootEventResponderInstance;
|
||||
const eventListener = responder.onRootEvent;
|
||||
if (eventListener !== undefined) {
|
||||
if (rootEventInstances !== undefined) {
|
||||
const rootEventComponentInstances = Array.from(rootEventInstances);
|
||||
|
||||
for (let i = 0; i < rootEventComponentInstances.length; i++) {
|
||||
const rootEventComponentInstance = rootEventComponentInstances[i];
|
||||
const {isHook, props, responder, state} = rootEventComponentInstance;
|
||||
const onRootEvent = responder.onRootEvent;
|
||||
if (onRootEvent !== undefined) {
|
||||
if (
|
||||
shouldSkipEventComponent(
|
||||
rootEventResponderInstance,
|
||||
rootEventComponentInstance,
|
||||
responder,
|
||||
null,
|
||||
isHook,
|
||||
@@ -651,9 +631,9 @@ function traverseAndHandleEventResponderInstances(
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
currentInstance = rootEventResponderInstance;
|
||||
currentInstance = rootEventComponentInstance;
|
||||
currentlyInHook = isHook;
|
||||
eventListener(responderEvent, eventResponderContext, props, state);
|
||||
onRootEvent(responderEvent, eventResponderContext, props, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -199,6 +199,7 @@ export type ReactFaricEvent = {
|
||||
};
|
||||
|
||||
export type ReactNativeResponderEvent = {
|
||||
currentTarget: null | ReactNativeEventTarget,
|
||||
nativeEvent: ReactFaricEvent,
|
||||
target: null | ReactNativeEventTarget,
|
||||
type: ReactNativeEventResponderEventType,
|
||||
@@ -229,9 +230,6 @@ export type ReactNativeResponderContext = {
|
||||
removeRootEventTypes: (
|
||||
rootEventTypes: Array<ReactNativeEventResponderEventType>,
|
||||
) => void,
|
||||
getEventCurrentTarget(
|
||||
event: ReactNativeResponderEvent,
|
||||
): ReactNativeEventTarget,
|
||||
setTimeout: (func: () => void, timeout: number) => number,
|
||||
clearTimeout: (timerId: number) => void,
|
||||
getTimeStamp: () => number,
|
||||
|
||||
@@ -28,6 +28,7 @@ export type PointerType =
|
||||
| 'trackpad';
|
||||
|
||||
export type ReactDOMResponderEvent = {
|
||||
currentTarget: null | Element | Document,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
passive: boolean,
|
||||
passiveSupported: boolean,
|
||||
@@ -75,7 +76,6 @@ export type ReactDOMResponderContext = {
|
||||
getFocusableElementsInScope(): Array<HTMLElement>,
|
||||
getActiveDocument(): Document,
|
||||
objectAssign: Function,
|
||||
getEventCurrentTarget(event: ReactDOMResponderEvent): Element,
|
||||
getTimeStamp: () => number,
|
||||
isTargetWithinHostComponent: (
|
||||
target: Element | Document,
|
||||
|
||||
Reference in New Issue
Block a user