mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
ModernEventSystem: fix event replaying (#18346)
* ModernEventSystem: fix event replaying
This commit is contained in:
+52
-50
@@ -302,60 +302,62 @@ export function dispatchEventForPluginEventSystem(
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// The below logic attempts to work out if we need to change
|
||||
// the target fiber to a different ancestor. We had similar logic
|
||||
// in the legacy event system, except the big difference between
|
||||
// systems is that the modern event system now has an event listener
|
||||
// attached to each React Root and React Portal Root. Together,
|
||||
// the DOM nodes representing these roots are the "rootContainer".
|
||||
// To figure out which ancestor instance we should use, we traverse
|
||||
// up the fiber tree from the target instance and attempt to find
|
||||
// root boundaries that match that of our current "rootContainer".
|
||||
// If we find that "rootContainer", we find the parent fiber
|
||||
// sub-tree for that root and make that our ancestor instance.
|
||||
let node = targetInst;
|
||||
if (targetInst !== null) {
|
||||
// The below logic attempts to work out if we need to change
|
||||
// the target fiber to a different ancestor. We had similar logic
|
||||
// in the legacy event system, except the big difference between
|
||||
// systems is that the modern event system now has an event listener
|
||||
// attached to each React Root and React Portal Root. Together,
|
||||
// the DOM nodes representing these roots are the "rootContainer".
|
||||
// To figure out which ancestor instance we should use, we traverse
|
||||
// up the fiber tree from the target instance and attempt to find
|
||||
// root boundaries that match that of our current "rootContainer".
|
||||
// If we find that "rootContainer", we find the parent fiber
|
||||
// sub-tree for that root and make that our ancestor instance.
|
||||
let node = targetInst;
|
||||
|
||||
while (true) {
|
||||
if (node === null) {
|
||||
return;
|
||||
}
|
||||
if (node.tag === HostRoot || node.tag === HostPortal) {
|
||||
const container = node.stateNode.containerInfo;
|
||||
if (isMatchingRootContainer(container, possibleTargetContainerNode)) {
|
||||
break;
|
||||
}
|
||||
if (node.tag === HostPortal) {
|
||||
// The target is a portal, but it's not the rootContainer we're looking for.
|
||||
// Normally portals handle their own events all the way down to the root.
|
||||
// So we should be able to stop now. However, we don't know if this portal
|
||||
// was part of *our* root.
|
||||
let grandNode = node.return;
|
||||
while (grandNode !== null) {
|
||||
if (grandNode.tag === HostRoot || grandNode.tag === HostPortal) {
|
||||
const grandContainer = grandNode.stateNode.containerInfo;
|
||||
if (
|
||||
isMatchingRootContainer(
|
||||
grandContainer,
|
||||
possibleTargetContainerNode,
|
||||
)
|
||||
) {
|
||||
// This is the rootContainer we're looking for and we found it as
|
||||
// a parent of the Portal. That means we can ignore it because the
|
||||
// Portal will bubble through to us.
|
||||
return;
|
||||
}
|
||||
}
|
||||
grandNode = grandNode.return;
|
||||
}
|
||||
}
|
||||
const parentSubtreeInst = getClosestInstanceFromNode(container);
|
||||
if (parentSubtreeInst === null) {
|
||||
while (true) {
|
||||
if (node === null) {
|
||||
return;
|
||||
}
|
||||
node = ancestorInst = parentSubtreeInst;
|
||||
continue;
|
||||
if (node.tag === HostRoot || node.tag === HostPortal) {
|
||||
const container = node.stateNode.containerInfo;
|
||||
if (isMatchingRootContainer(container, possibleTargetContainerNode)) {
|
||||
break;
|
||||
}
|
||||
if (node.tag === HostPortal) {
|
||||
// The target is a portal, but it's not the rootContainer we're looking for.
|
||||
// Normally portals handle their own events all the way down to the root.
|
||||
// So we should be able to stop now. However, we don't know if this portal
|
||||
// was part of *our* root.
|
||||
let grandNode = node.return;
|
||||
while (grandNode !== null) {
|
||||
if (grandNode.tag === HostRoot || grandNode.tag === HostPortal) {
|
||||
const grandContainer = grandNode.stateNode.containerInfo;
|
||||
if (
|
||||
isMatchingRootContainer(
|
||||
grandContainer,
|
||||
possibleTargetContainerNode,
|
||||
)
|
||||
) {
|
||||
// This is the rootContainer we're looking for and we found it as
|
||||
// a parent of the Portal. That means we can ignore it because the
|
||||
// Portal will bubble through to us.
|
||||
return;
|
||||
}
|
||||
}
|
||||
grandNode = grandNode.return;
|
||||
}
|
||||
}
|
||||
const parentSubtreeInst = getClosestInstanceFromNode(container);
|
||||
if (parentSubtreeInst === null) {
|
||||
return;
|
||||
}
|
||||
node = ancestorInst = parentSubtreeInst;
|
||||
continue;
|
||||
}
|
||||
node = node.return;
|
||||
}
|
||||
node = node.return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+48
-27
@@ -127,7 +127,7 @@ type QueuedReplayableEvent = {|
|
||||
topLevelType: DOMTopLevelEventType,
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
targetContainer: EventTarget | null,
|
||||
targetContainers: Array<EventTarget>,
|
||||
|};
|
||||
|
||||
let hasScheduledReplayAttempt = false;
|
||||
@@ -294,7 +294,7 @@ function createQueuedReplayableEvent(
|
||||
topLevelType,
|
||||
eventSystemFlags: eventSystemFlags | IS_REPLAYED,
|
||||
nativeEvent,
|
||||
targetContainer,
|
||||
targetContainers: targetContainer !== null ? [targetContainer] : [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -402,9 +402,16 @@ function accumulateOrCreateContinuousQueuedReplayableEvent(
|
||||
}
|
||||
// If we have already queued this exact event, then it's because
|
||||
// the different event systems have different DOM event listeners.
|
||||
// We can accumulate the flags and store a single event to be
|
||||
// replayed.
|
||||
// We can accumulate the flags, and the targetContainers, and
|
||||
// store a single event to be replayed.
|
||||
existingQueuedEvent.eventSystemFlags |= eventSystemFlags;
|
||||
const targetContainers = existingQueuedEvent.targetContainers;
|
||||
if (
|
||||
targetContainer !== null &&
|
||||
targetContainers.indexOf(targetContainer) === -1
|
||||
) {
|
||||
targetContainers.push(targetContainer);
|
||||
}
|
||||
return existingQueuedEvent;
|
||||
}
|
||||
|
||||
@@ -555,20 +562,26 @@ function attemptReplayContinuousQueuedEvent(
|
||||
if (queuedEvent.blockedOn !== null) {
|
||||
return false;
|
||||
}
|
||||
let nextBlockedOn = attemptToDispatchEvent(
|
||||
queuedEvent.topLevelType,
|
||||
queuedEvent.eventSystemFlags,
|
||||
queuedEvent.targetContainer,
|
||||
queuedEvent.nativeEvent,
|
||||
);
|
||||
if (nextBlockedOn !== null) {
|
||||
// We're still blocked. Try again later.
|
||||
let fiber = getInstanceFromNode(nextBlockedOn);
|
||||
if (fiber !== null) {
|
||||
attemptContinuousHydration(fiber);
|
||||
let targetContainers = queuedEvent.targetContainers;
|
||||
while (targetContainers.length > 0) {
|
||||
let targetContainer = targetContainers[0];
|
||||
let nextBlockedOn = attemptToDispatchEvent(
|
||||
queuedEvent.topLevelType,
|
||||
queuedEvent.eventSystemFlags,
|
||||
targetContainer,
|
||||
queuedEvent.nativeEvent,
|
||||
);
|
||||
if (nextBlockedOn !== null) {
|
||||
// We're still blocked. Try again later.
|
||||
let fiber = getInstanceFromNode(nextBlockedOn);
|
||||
if (fiber !== null) {
|
||||
attemptContinuousHydration(fiber);
|
||||
}
|
||||
queuedEvent.blockedOn = nextBlockedOn;
|
||||
return false;
|
||||
}
|
||||
queuedEvent.blockedOn = nextBlockedOn;
|
||||
return false;
|
||||
// This target container was successfully dispatched. Try the next.
|
||||
targetContainers.shift();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -598,16 +611,24 @@ function replayUnblockedEvents() {
|
||||
}
|
||||
break;
|
||||
}
|
||||
let nextBlockedOn = attemptToDispatchEvent(
|
||||
nextDiscreteEvent.topLevelType,
|
||||
nextDiscreteEvent.eventSystemFlags,
|
||||
nextDiscreteEvent.targetContainer,
|
||||
nextDiscreteEvent.nativeEvent,
|
||||
);
|
||||
if (nextBlockedOn !== null) {
|
||||
// We're still blocked. Try again later.
|
||||
nextDiscreteEvent.blockedOn = nextBlockedOn;
|
||||
} else {
|
||||
let targetContainers = nextDiscreteEvent.targetContainers;
|
||||
while (targetContainers.length > 0) {
|
||||
let targetContainer = targetContainers[0];
|
||||
let nextBlockedOn = attemptToDispatchEvent(
|
||||
nextDiscreteEvent.topLevelType,
|
||||
nextDiscreteEvent.eventSystemFlags,
|
||||
targetContainer,
|
||||
nextDiscreteEvent.nativeEvent,
|
||||
);
|
||||
if (nextBlockedOn !== null) {
|
||||
// We're still blocked. Try again later.
|
||||
nextDiscreteEvent.blockedOn = nextBlockedOn;
|
||||
break;
|
||||
}
|
||||
// This target container was successfully dispatched. Try the next.
|
||||
targetContainers.shift();
|
||||
}
|
||||
if (nextDiscreteEvent.blockedOn === null) {
|
||||
// We've successfully replayed the first event. Let's try the next one.
|
||||
queuedDiscreteEvents.shift();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user