@samitouri / QOS-React / commits / 9e7f334c71

ModernEventSystem: fix event replaying (#18346)

* ModernEventSystem: fix event replaying

Dominic Gannaway committed Mar 19, 2020 at 20:44 UTC 9e7f334c7159b10900a8d197c3df226d32efdc96
2 files changed +97 -74
packages/react-dom/src/events/DOMModernPluginEventSystem.js
+49 -47
@@ -302,60 +302,62 @@ export function dispatchEventForPluginEventSystem(
302 ) {
303 return;
304 }
305 - // The below logic attempts to work out if we need to change
306 - // the target fiber to a different ancestor. We had similar logic
307 - // in the legacy event system, except the big difference between
308 - // systems is that the modern event system now has an event listener
309 - // attached to each React Root and React Portal Root. Together,
310 - // the DOM nodes representing these roots are the "rootContainer".
311 - // To figure out which ancestor instance we should use, we traverse
312 - // up the fiber tree from the target instance and attempt to find
313 - // root boundaries that match that of our current "rootContainer".
314 - // If we find that "rootContainer", we find the parent fiber
315 - // sub-tree for that root and make that our ancestor instance.
316 - let node = targetInst;
305 + if (targetInst !== null) {
306 + // The below logic attempts to work out if we need to change
307 + // the target fiber to a different ancestor. We had similar logic
308 + // in the legacy event system, except the big difference between
309 + // systems is that the modern event system now has an event listener
310 + // attached to each React Root and React Portal Root. Together,
311 + // the DOM nodes representing these roots are the "rootContainer".
312 + // To figure out which ancestor instance we should use, we traverse
313 + // up the fiber tree from the target instance and attempt to find
314 + // root boundaries that match that of our current "rootContainer".
315 + // If we find that "rootContainer", we find the parent fiber
316 + // sub-tree for that root and make that our ancestor instance.
317 + let node = targetInst;
318
318 - while (true) {
319 - if (node === null) {
320 - return;
321 - }
322 - if (node.tag === HostRoot || node.tag === HostPortal) {
323 - const container = node.stateNode.containerInfo;
324 - if (isMatchingRootContainer(container, possibleTargetContainerNode)) {
325 - break;
319 + while (true) {
320 + if (node === null) {
321 + return;
322 }
327 - if (node.tag === HostPortal) {
328 - // The target is a portal, but it's not the rootContainer we're looking for.
329 - // Normally portals handle their own events all the way down to the root.
330 - // So we should be able to stop now. However, we don't know if this portal
331 - // was part of *our* root.
332 - let grandNode = node.return;
333 - while (grandNode !== null) {
334 - if (grandNode.tag === HostRoot || grandNode.tag === HostPortal) {
335 - const grandContainer = grandNode.stateNode.containerInfo;
336 - if (
337 - isMatchingRootContainer(
338 - grandContainer,
339 - possibleTargetContainerNode,
340 - )
341 - ) {
342 - // This is the rootContainer we're looking for and we found it as
343 - // a parent of the Portal. That means we can ignore it because the
344 - // Portal will bubble through to us.
345 - return;
323 + if (node.tag === HostRoot || node.tag === HostPortal) {
324 + const container = node.stateNode.containerInfo;
325 + if (isMatchingRootContainer(container, possibleTargetContainerNode)) {
326 + break;
327 + }
328 + if (node.tag === HostPortal) {
329 + // The target is a portal, but it's not the rootContainer we're looking for.
330 + // Normally portals handle their own events all the way down to the root.
331 + // So we should be able to stop now. However, we don't know if this portal
332 + // was part of *our* root.
333 + let grandNode = node.return;
334 + while (grandNode !== null) {
335 + if (grandNode.tag === HostRoot || grandNode.tag === HostPortal) {
336 + const grandContainer = grandNode.stateNode.containerInfo;
337 + if (
338 + isMatchingRootContainer(
339 + grandContainer,
340 + possibleTargetContainerNode,
341 + )
342 + ) {
343 + // This is the rootContainer we're looking for and we found it as
344 + // a parent of the Portal. That means we can ignore it because the
345 + // Portal will bubble through to us.
346 + return;
347 + }
348 }
349 + grandNode = grandNode.return;
350 }
348 - grandNode = grandNode.return;
351 }
352 + const parentSubtreeInst = getClosestInstanceFromNode(container);
353 + if (parentSubtreeInst === null) {
354 + return;
355 + }
356 + node = ancestorInst = parentSubtreeInst;
357 + continue;
358 }
351 - const parentSubtreeInst = getClosestInstanceFromNode(container);
352 - if (parentSubtreeInst === null) {
353 - return;
354 - }
355 - node = ancestorInst = parentSubtreeInst;
356 - continue;
359 + node = node.return;
360 }
358 - node = node.return;
361 }
362 }
363
packages/react-dom/src/events/ReactDOMEventReplaying.js
+48 -27
@@ -127,7 +127,7 @@ type QueuedReplayableEvent = {|
127 topLevelType: DOMTopLevelEventType,
128 eventSystemFlags: EventSystemFlags,
129 nativeEvent: AnyNativeEvent,
130 - targetContainer: EventTarget | null,
130 + targetContainers: Array<EventTarget>,
131 |};
132
133 let hasScheduledReplayAttempt = false;
@@ -294,7 +294,7 @@ function createQueuedReplayableEvent(
294 topLevelType,
295 eventSystemFlags: eventSystemFlags | IS_REPLAYED,
296 nativeEvent,
297 - targetContainer,
297 + targetContainers: targetContainer !== null ? [targetContainer] : [],
298 };
299 }
300
@@ -402,9 +402,16 @@ function accumulateOrCreateContinuousQueuedReplayableEvent(
402 }
403 // If we have already queued this exact event, then it's because
404 // the different event systems have different DOM event listeners.
405 - // We can accumulate the flags and store a single event to be
406 - // replayed.
405 + // We can accumulate the flags, and the targetContainers, and
406 + // store a single event to be replayed.
407 existingQueuedEvent.eventSystemFlags |= eventSystemFlags;
408 + const targetContainers = existingQueuedEvent.targetContainers;
409 + if (
410 + targetContainer !== null &&
411 + targetContainers.indexOf(targetContainer) === -1
412 + ) {
413 + targetContainers.push(targetContainer);
414 + }
415 return existingQueuedEvent;
416 }
417
@@ -555,20 +562,26 @@ function attemptReplayContinuousQueuedEvent(
562 if (queuedEvent.blockedOn !== null) {
563 return false;
564 }
558 - let nextBlockedOn = attemptToDispatchEvent(
559 - queuedEvent.topLevelType,
560 - queuedEvent.eventSystemFlags,
561 - queuedEvent.targetContainer,
562 - queuedEvent.nativeEvent,
563 - );
564 - if (nextBlockedOn !== null) {
565 - // We're still blocked. Try again later.
566 - let fiber = getInstanceFromNode(nextBlockedOn);
567 - if (fiber !== null) {
568 - attemptContinuousHydration(fiber);
565 + let targetContainers = queuedEvent.targetContainers;
566 + while (targetContainers.length > 0) {
567 + let targetContainer = targetContainers[0];
568 + let nextBlockedOn = attemptToDispatchEvent(
569 + queuedEvent.topLevelType,
570 + queuedEvent.eventSystemFlags,
571 + targetContainer,
572 + queuedEvent.nativeEvent,
573 + );
574 + if (nextBlockedOn !== null) {
575 + // We're still blocked. Try again later.
576 + let fiber = getInstanceFromNode(nextBlockedOn);
577 + if (fiber !== null) {
578 + attemptContinuousHydration(fiber);
579 + }
580 + queuedEvent.blockedOn = nextBlockedOn;
581 + return false;
582 }
570 - queuedEvent.blockedOn = nextBlockedOn;
571 - return false;
583 + // This target container was successfully dispatched. Try the next.
584 + targetContainers.shift();
585 }
586 return true;
587 }
@@ -598,16 +611,24 @@ function replayUnblockedEvents() {
611 }
612 break;
613 }
601 - let nextBlockedOn = attemptToDispatchEvent(
602 - nextDiscreteEvent.topLevelType,
603 - nextDiscreteEvent.eventSystemFlags,
604 - nextDiscreteEvent.targetContainer,
605 - nextDiscreteEvent.nativeEvent,
606 - );
607 - if (nextBlockedOn !== null) {
608 - // We're still blocked. Try again later.
609 - nextDiscreteEvent.blockedOn = nextBlockedOn;
610 - } else {
614 + let targetContainers = nextDiscreteEvent.targetContainers;
615 + while (targetContainers.length > 0) {
616 + let targetContainer = targetContainers[0];
617 + let nextBlockedOn = attemptToDispatchEvent(
618 + nextDiscreteEvent.topLevelType,
619 + nextDiscreteEvent.eventSystemFlags,
620 + targetContainer,
621 + nextDiscreteEvent.nativeEvent,
622 + );
623 + if (nextBlockedOn !== null) {
624 + // We're still blocked. Try again later.
625 + nextDiscreteEvent.blockedOn = nextBlockedOn;
626 + break;
627 + }
628 + // This target container was successfully dispatched. Try the next.
629 + targetContainers.shift();
630 + }
631 + if (nextDiscreteEvent.blockedOn === null) {
632 // We've successfully replayed the first event. Let's try the next one.
633 queuedDiscreteEvents.shift();
634 }