@samitouri / QOS-React-2 / commits / a4bed46969

[Transition Tracing] Add Tracing Markers (#24686)

This PR adds support for Tracing Markers as well as onTracingMarkerComplete

Luna Ruan committed Jun 27, 2022 at 12:46 UTC a4bed469697a1e23c398fe80228df31ef6d4b612
14 files changed +543 -12
packages/react-reconciler/src/ReactFiber.new.js
+6
@@ -19,6 +19,7 @@ import type {
19 OffscreenProps,
20 OffscreenInstance,
21 } from './ReactFiberOffscreenComponent';
22 +import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.new';
23
24 import {
25 createRootStrictEffectsByDefault,
@@ -757,6 +758,11 @@ export function createFiberFromTracingMarker(
758 const fiber = createFiber(TracingMarkerComponent, pendingProps, key, mode);
759 fiber.elementType = REACT_TRACING_MARKER_TYPE;
760 fiber.lanes = lanes;
761 + const tracingMarkerInstance: TracingMarkerInstance = {
762 + transitions: null,
763 + pendingSuspenseBoundaries: null,
764 + };
765 + fiber.stateNode = tracingMarkerInstance;
766 return fiber;
767 }
768
packages/react-reconciler/src/ReactFiber.old.js
+6
@@ -19,6 +19,7 @@ import type {
19 OffscreenProps,
20 OffscreenInstance,
21 } from './ReactFiberOffscreenComponent';
22 +import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.old';
23
24 import {
25 createRootStrictEffectsByDefault,
@@ -757,6 +758,11 @@ export function createFiberFromTracingMarker(
758 const fiber = createFiber(TracingMarkerComponent, pendingProps, key, mode);
759 fiber.elementType = REACT_TRACING_MARKER_TYPE;
760 fiber.lanes = lanes;
761 + const tracingMarkerInstance: TracingMarkerInstance = {
762 + transitions: null,
763 + pendingSuspenseBoundaries: null,
764 + };
765 + fiber.stateNode = tracingMarkerInstance;
766 return fiber;
767 }
768
packages/react-reconciler/src/ReactFiberBeginWork.new.js
+27 -4
@@ -35,6 +35,7 @@ import type {
35 } from './ReactFiberCacheComponent.new';
36 import type {UpdateQueue} from './ReactFiberClassUpdateQueue.new';
37 import type {RootState} from './ReactFiberRoot.new';
38 +import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.new';
39 import {
40 enableSuspenseAvoidThisFallback,
41 enableCPUSuspense,
@@ -255,9 +256,12 @@ import {
256 getSuspendedCache,
257 pushTransition,
258 getOffscreenDeferredCache,
258 - getSuspendedTransitions,
259 + getPendingTransitions,
260 } from './ReactFiberTransition.new';
260 -import {pushTracingMarker} from './ReactFiberTracingMarkerComponent.new';
261 +import {
262 + getTracingMarkers,
263 + pushTracingMarker,
264 +} from './ReactFiberTracingMarkerComponent.new';
265
266 const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
267
@@ -891,6 +895,20 @@ function updateTracingMarkerComponent(
895 return null;
896 }
897
898 + // TODO: (luna) Only update the tracing marker if it's newly rendered or it's name changed.
899 + // A tracing marker is only associated with the transitions that rendered
900 + // or updated it, so we can create a new set of transitions each time
901 + if (current === null) {
902 + const currentTransitions = getPendingTransitions();
903 + if (currentTransitions !== null) {
904 + const markerInstance: TracingMarkerInstance = {
905 + transitions: new Set(currentTransitions),
906 + pendingSuspenseBoundaries: new Map(),
907 + };
908 + workInProgress.stateNode = markerInstance;
909 + }
910 + }
911 +
912 pushTracingMarker(workInProgress);
913 const nextChildren = workInProgress.pendingProps.children;
914 reconcileChildren(current, workInProgress, nextChildren, renderLanes);
@@ -2093,10 +2111,13 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) {
2111 );
2112 workInProgress.memoizedState = SUSPENDED_MARKER;
2113 if (enableTransitionTracing) {
2096 - const currentTransitions = getSuspendedTransitions();
2114 + const currentTransitions = getPendingTransitions();
2115 if (currentTransitions !== null) {
2116 + // If there are no transitions, we don't need to keep track of tracing markers
2117 + const currentTracingMarkers = getTracingMarkers();
2118 const primaryChildUpdateQueue: OffscreenQueue = {
2119 transitions: currentTransitions,
2120 + tracingMarkers: currentTracingMarkers,
2121 };
2122 primaryChildFragment.updateQueue = primaryChildUpdateQueue;
2123 }
@@ -2177,10 +2198,12 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) {
2198 ? mountSuspenseOffscreenState(renderLanes)
2199 : updateSuspenseOffscreenState(prevOffscreenState, renderLanes);
2200 if (enableTransitionTracing) {
2180 - const currentTransitions = getSuspendedTransitions();
2201 + const currentTransitions = getPendingTransitions();
2202 if (currentTransitions !== null) {
2203 + const currentTracingMarkers = getTracingMarkers();
2204 const primaryChildUpdateQueue: OffscreenQueue = {
2205 transitions: currentTransitions,
2206 + tracingMarkers: currentTracingMarkers,
2207 };
2208 primaryChildFragment.updateQueue = primaryChildUpdateQueue;
2209 }
packages/react-reconciler/src/ReactFiberBeginWork.old.js
+27 -4
@@ -35,6 +35,7 @@ import type {
35 } from './ReactFiberCacheComponent.old';
36 import type {UpdateQueue} from './ReactFiberClassUpdateQueue.old';
37 import type {RootState} from './ReactFiberRoot.old';
38 +import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.old';
39 import {
40 enableSuspenseAvoidThisFallback,
41 enableCPUSuspense,
@@ -255,9 +256,12 @@ import {
256 getSuspendedCache,
257 pushTransition,
258 getOffscreenDeferredCache,
258 - getSuspendedTransitions,
259 + getPendingTransitions,
260 } from './ReactFiberTransition.old';
260 -import {pushTracingMarker} from './ReactFiberTracingMarkerComponent.old';
261 +import {
262 + getTracingMarkers,
263 + pushTracingMarker,
264 +} from './ReactFiberTracingMarkerComponent.old';
265
266 const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
267
@@ -891,6 +895,20 @@ function updateTracingMarkerComponent(
895 return null;
896 }
897
898 + // TODO: (luna) Only update the tracing marker if it's newly rendered or it's name changed.
899 + // A tracing marker is only associated with the transitions that rendered
900 + // or updated it, so we can create a new set of transitions each time
901 + if (current === null) {
902 + const currentTransitions = getPendingTransitions();
903 + if (currentTransitions !== null) {
904 + const markerInstance: TracingMarkerInstance = {
905 + transitions: new Set(currentTransitions),
906 + pendingSuspenseBoundaries: new Map(),
907 + };
908 + workInProgress.stateNode = markerInstance;
909 + }
910 + }
911 +
912 pushTracingMarker(workInProgress);
913 const nextChildren = workInProgress.pendingProps.children;
914 reconcileChildren(current, workInProgress, nextChildren, renderLanes);
@@ -2093,10 +2111,13 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) {
2111 );
2112 workInProgress.memoizedState = SUSPENDED_MARKER;
2113 if (enableTransitionTracing) {
2096 - const currentTransitions = getSuspendedTransitions();
2114 + const currentTransitions = getPendingTransitions();
2115 if (currentTransitions !== null) {
2116 + // If there are no transitions, we don't need to keep track of tracing markers
2117 + const currentTracingMarkers = getTracingMarkers();
2118 const primaryChildUpdateQueue: OffscreenQueue = {
2119 transitions: currentTransitions,
2120 + tracingMarkers: currentTracingMarkers,
2121 };
2122 primaryChildFragment.updateQueue = primaryChildUpdateQueue;
2123 }
@@ -2177,10 +2198,12 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) {
2198 ? mountSuspenseOffscreenState(renderLanes)
2199 : updateSuspenseOffscreenState(prevOffscreenState, renderLanes);
2200 if (enableTransitionTracing) {
2180 - const currentTransitions = getSuspendedTransitions();
2201 + const currentTransitions = getPendingTransitions();
2202 if (currentTransitions !== null) {
2203 + const currentTracingMarkers = getTracingMarkers();
2204 const primaryChildUpdateQueue: OffscreenQueue = {
2205 transitions: currentTransitions,
2206 + tracingMarkers: currentTracingMarkers,
2207 };
2208 primaryChildFragment.updateQueue = primaryChildUpdateQueue;
2209 }
packages/react-reconciler/src/ReactFiberCommitWork.new.js
+41
@@ -138,6 +138,7 @@ import {
138 restorePendingUpdaters,
139 addTransitionStartCallbackToPendingTransition,
140 addTransitionCompleteCallbackToPendingTransition,
141 + addMarkerCompleteCallbackToPendingTransition,
142 setIsRunningInsertionEffect,
143 } from './ReactFiberWorkLoop.new';
144 import {
@@ -2910,6 +2911,7 @@ function commitPassiveMountOnFiber(
2911 instance.transitions = prevTransitions = new Set();
2912 }
2913
2914 + // TODO(luna): Combine the root code with the tracing marker code
2915 if (transitions !== null) {
2916 transitions.forEach(transition => {
2917 // Add all the transitions saved in the update queue during
@@ -2931,6 +2933,23 @@ function commitPassiveMountOnFiber(
2933 }
2934 });
2935 }
2936 +
2937 + const tracingMarkers = queue.tracingMarkers;
2938 + if (tracingMarkers !== null) {
2939 + tracingMarkers.forEach(marker => {
2940 + const markerInstance = marker.stateNode;
2941 + // There should only be a few tracing marker transitions because
2942 + // they should be only associated with the transition that
2943 + // caused them
2944 + markerInstance.transitions.forEach(transition => {
2945 + if (instance.transitions.has(transition)) {
2946 + instance.pendingMarkers.add(
2947 + markerInstance.pendingSuspenseBoundaries,
2948 + );
2949 + }
2950 + });
2951 + });
2952 + }
2953 }
2954
2955 commitTransitionProgress(finishedWork);
@@ -2967,6 +2986,28 @@ function commitPassiveMountOnFiber(
2986 }
2987 break;
2988 }
2989 + case TracingMarkerComponent: {
2990 + if (enableTransitionTracing) {
2991 + // Get the transitions that were initiatized during the render
2992 + // and add a start transition callback for each of them
2993 + const instance = finishedWork.stateNode;
2994 + if (
2995 + instance.pendingSuspenseBoundaries === null ||
2996 + instance.pendingSuspenseBoundaries.size === 0
2997 + ) {
2998 + instance.transitions.forEach(transition => {
2999 + addMarkerCompleteCallbackToPendingTransition({
3000 + transitionName: transition.name,
3001 + startTime: transition.startTime,
3002 + markerName: finishedWork.memoizedProps.name,
3003 + });
3004 + });
3005 + instance.transitions = null;
3006 + instance.pendingSuspenseBoundaries = null;
3007 + }
3008 + }
3009 + break;
3010 + }
3011 }
3012 }
3013
packages/react-reconciler/src/ReactFiberCommitWork.old.js
+41
@@ -138,6 +138,7 @@ import {
138 restorePendingUpdaters,
139 addTransitionStartCallbackToPendingTransition,
140 addTransitionCompleteCallbackToPendingTransition,
141 + addMarkerCompleteCallbackToPendingTransition,
142 setIsRunningInsertionEffect,
143 } from './ReactFiberWorkLoop.old';
144 import {
@@ -2910,6 +2911,7 @@ function commitPassiveMountOnFiber(
2911 instance.transitions = prevTransitions = new Set();
2912 }
2913
2914 + // TODO(luna): Combine the root code with the tracing marker code
2915 if (transitions !== null) {
2916 transitions.forEach(transition => {
2917 // Add all the transitions saved in the update queue during
@@ -2931,6 +2933,23 @@ function commitPassiveMountOnFiber(
2933 }
2934 });
2935 }
2936 +
2937 + const tracingMarkers = queue.tracingMarkers;
2938 + if (tracingMarkers !== null) {
2939 + tracingMarkers.forEach(marker => {
2940 + const markerInstance = marker.stateNode;
2941 + // There should only be a few tracing marker transitions because
2942 + // they should be only associated with the transition that
2943 + // caused them
2944 + markerInstance.transitions.forEach(transition => {
2945 + if (instance.transitions.has(transition)) {
2946 + instance.pendingMarkers.add(
2947 + markerInstance.pendingSuspenseBoundaries,
2948 + );
2949 + }
2950 + });
2951 + });
2952 + }
2953 }
2954
2955 commitTransitionProgress(finishedWork);
@@ -2967,6 +2986,28 @@ function commitPassiveMountOnFiber(
2986 }
2987 break;
2988 }
2989 + case TracingMarkerComponent: {
2990 + if (enableTransitionTracing) {
2991 + // Get the transitions that were initiatized during the render
2992 + // and add a start transition callback for each of them
2993 + const instance = finishedWork.stateNode;
2994 + if (
2995 + instance.pendingSuspenseBoundaries === null ||
2996 + instance.pendingSuspenseBoundaries.size === 0
2997 + ) {
2998 + instance.transitions.forEach(transition => {
2999 + addMarkerCompleteCallbackToPendingTransition({
3000 + transitionName: transition.name,
3001 + startTime: transition.startTime,
3002 + markerName: finishedWork.memoizedProps.name,
3003 + });
3004 + });
3005 + instance.transitions = null;
3006 + instance.pendingSuspenseBoundaries = null;
3007 + }
3008 + }
3009 + break;
3010 + }
3011 }
3012 }
3013
packages/react-reconciler/src/ReactFiberCompleteWork.new.js
+10 -1
@@ -1581,9 +1581,18 @@ function completeWork(
1581 }
1582 case TracingMarkerComponent: {
1583 if (enableTransitionTracing) {
1584 - // Bubble subtree flags before so we can set the flag property
1584 popTracingMarker(workInProgress);
1585 bubbleProperties(workInProgress);
1586 +
1587 + if (
1588 + current === null ||
1589 + (workInProgress.subtreeFlags & Visibility) !== NoFlags
1590 + ) {
1591 + // If any of our suspense children toggle visibility, this means that
1592 + // the pending boundaries array needs to be updated, which we only
1593 + // do in the passive phase.
1594 + workInProgress.flags |= Passive;
1595 + }
1596 }
1597 return null;
1598 }
packages/react-reconciler/src/ReactFiberCompleteWork.old.js
+10 -1
@@ -1581,9 +1581,18 @@ function completeWork(
1581 }
1582 case TracingMarkerComponent: {
1583 if (enableTransitionTracing) {
1584 - // Bubble subtree flags before so we can set the flag property
1584 popTracingMarker(workInProgress);
1585 bubbleProperties(workInProgress);
1586 +
1587 + if (
1588 + current === null ||
1589 + (workInProgress.subtreeFlags & Visibility) !== NoFlags
1590 + ) {
1591 + // If any of our suspense children toggle visibility, this means that
1592 + // the pending boundaries array needs to be updated, which we only
1593 + // do in the passive phase.
1594 + workInProgress.flags |= Passive;
1595 + }
1596 }
1597 return null;
1598 }
packages/react-reconciler/src/ReactFiberOffscreenComponent.js
+2
@@ -8,6 +8,7 @@
8 */
9
10 import type {ReactNodeList, OffscreenMode} from 'shared/ReactTypes';
11 +import type {Fiber} from './ReactInternalTypes';
12 import type {Lanes} from './ReactFiberLane.old';
13 import type {SpawnedCachePool} from './ReactFiberCacheComponent.new';
14 import type {
@@ -38,6 +39,7 @@ export type OffscreenState = {|
39
40 export type OffscreenQueue = {|
41 transitions: Array<Transition> | null,
42 + tracingMarkers: Array<Fiber> | null,
43 |} | null;
44
45 export type OffscreenInstance = {|
packages/react-reconciler/src/ReactFiberTracingMarkerComponent.new.js
+5
@@ -39,6 +39,11 @@ export type BatchConfigTransition = {
39 _updatedFibers?: Set<Fiber>,
40 };
41
42 +export type TracingMarkerInstance = {|
43 + pendingSuspenseBoundaries: PendingSuspenseBoundaries | null,
44 + transitions: Set<Transition> | null,
45 +|} | null;
46 +
47 export type PendingSuspenseBoundaries = Map<OffscreenInstance, SuspenseInfo>;
48
49 export function processTransitionCallbacks(
packages/react-reconciler/src/ReactFiberTracingMarkerComponent.old.js
+5
@@ -39,6 +39,11 @@ export type BatchConfigTransition = {
39 _updatedFibers?: Set<Fiber>,
40 };
41
42 +export type TracingMarkerInstance = {|
43 + pendingSuspenseBoundaries: PendingSuspenseBoundaries | null,
44 + transitions: Set<Transition> | null,
45 +|} | null;
46 +
47 export type PendingSuspenseBoundaries = Map<OffscreenInstance, SuspenseInfo>;
48
49 export function processTransitionCallbacks(
packages/react-reconciler/src/ReactFiberTransition.new.js
+1 -1
@@ -149,7 +149,7 @@ export function popTransition(workInProgress: Fiber, current: Fiber | null) {
149 }
150 }
151
152 -export function getSuspendedTransitions(): Array<Transition> | null {
152 +export function getPendingTransitions(): Array<Transition> | null {
153 if (!enableTransitionTracing) {
154 return null;
155 }
packages/react-reconciler/src/ReactFiberTransition.old.js
+1 -1
@@ -149,7 +149,7 @@ export function popTransition(workInProgress: Fiber, current: Fiber | null) {
149 }
150 }
151
152 -export function getSuspendedTransitions(): Array<Transition> | null {
152 +export function getPendingTransitions(): Array<Transition> | null {
153 if (!enableTransitionTracing) {
154 return null;
155 }
packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js
+361
@@ -477,4 +477,365 @@ describe('ReactInteractionTracing', () => {
477 ]);
478 });
479 });
480 +
481 + // @gate enableTransitionTracing
482 + it('should correctly trace interactions for tracing markers complete', async () => {
483 + const transitionCallbacks = {
484 + onTransitionStart: (name, startTime) => {
485 + Scheduler.unstable_yieldValue(
486 + `onTransitionStart(${name}, ${startTime})`,
487 + );
488 + },
489 + onTransitionComplete: (name, startTime, endTime) => {
490 + Scheduler.unstable_yieldValue(
491 + `onTransitionComplete(${name}, ${startTime}, ${endTime})`,
492 + );
493 + },
494 + onMarkerComplete: (transitioName, markerName, startTime, endTime) => {
495 + Scheduler.unstable_yieldValue(
496 + `onMarkerComplete(${transitioName}, ${markerName}, ${startTime}, ${endTime})`,
497 + );
498 + },
499 + };
500 + let navigateToPageTwo;
501 + function App() {
502 + const [navigate, setNavigate] = useState(false);
503 + navigateToPageTwo = () => {
504 + setNavigate(true);
505 + };
506 +
507 + return (
508 + <div>
509 + {navigate ? (
510 + <Suspense
511 + fallback={<Text text="Loading..." />}
512 + name="suspense page">
513 + <AsyncText text="Page Two" />
514 + <React.unstable_TracingMarker name="sync marker" />
515 + <React.unstable_TracingMarker name="async marker">
516 + <Suspense
517 + fallback={<Text text="Loading..." />}
518 + name="marker suspense">
519 + <AsyncText text="Marker Text" />
520 + </Suspense>
521 + </React.unstable_TracingMarker>
522 + </Suspense>
523 + ) : (
524 + <Text text="Page One" />
525 + )}
526 + </div>
527 + );
528 + }
529 +
530 + const root = ReactNoop.createRoot({transitionCallbacks});
531 + await act(async () => {
532 + root.render(<App />);
533 + ReactNoop.expire(1000);
534 + await advanceTimers(1000);
535 +
536 + expect(Scheduler).toFlushAndYield(['Page One']);
537 + });
538 +
539 + await act(async () => {
540 + startTransition(() => navigateToPageTwo(), {name: 'page transition'});
541 +
542 + ReactNoop.expire(1000);
543 + await advanceTimers(1000);
544 +
545 + expect(Scheduler).toFlushAndYield([
546 + 'Suspend [Page Two]',
547 + 'Suspend [Marker Text]',
548 + 'Loading...',
549 + 'Loading...',
550 + 'onTransitionStart(page transition, 1000)',
551 + ]);
552 +
553 + ReactNoop.expire(1000);
554 + await advanceTimers(1000);
555 + await resolveText('Page Two');
556 +
557 + expect(Scheduler).toFlushAndYield([
558 + 'Page Two',
559 + 'Suspend [Marker Text]',
560 + 'Loading...',
561 + 'onMarkerComplete(page transition, sync marker, 1000, 3000)',
562 + ]);
563 +
564 + ReactNoop.expire(1000);
565 + await advanceTimers(1000);
566 + await resolveText('Marker Text');
567 +
568 + expect(Scheduler).toFlushAndYield([
569 + 'Marker Text',
570 + 'onMarkerComplete(page transition, async marker, 1000, 4000)',
571 + 'onTransitionComplete(page transition, 1000, 4000)',
572 + ]);
573 + });
574 + });
575 +
576 + // @gate enableTransitionTracing
577 + it('trace interaction with multiple tracing markers', async () => {
578 + const transitionCallbacks = {
579 + onTransitionStart: (name, startTime) => {
580 + Scheduler.unstable_yieldValue(
581 + `onTransitionStart(${name}, ${startTime})`,
582 + );
583 + },
584 + onTransitionComplete: (name, startTime, endTime) => {
585 + Scheduler.unstable_yieldValue(
586 + `onTransitionComplete(${name}, ${startTime}, ${endTime})`,
587 + );
588 + },
589 + onMarkerComplete: (transitioName, markerName, startTime, endTime) => {
590 + Scheduler.unstable_yieldValue(
591 + `onMarkerComplete(${transitioName}, ${markerName}, ${startTime}, ${endTime})`,
592 + );
593 + },
594 + };
595 +
596 + let navigateToPageTwo;
597 + function App() {
598 + const [navigate, setNavigate] = useState(false);
599 + navigateToPageTwo = () => {
600 + setNavigate(true);
601 + };
602 +
603 + return (
604 + <div>
605 + {navigate ? (
606 + <React.unstable_TracingMarker name="outer marker">
607 + <Suspense fallback={<Text text="Outer..." />}>
608 + <AsyncText text="Outer Text" />
609 + <Suspense fallback={<Text text="Inner One..." />}>
610 + <React.unstable_TracingMarker name="marker one">
611 + <AsyncText text="Inner Text One" />
612 + </React.unstable_TracingMarker>
613 + </Suspense>
614 + <Suspense fallback={<Text text="Inner Two..." />}>
615 + <React.unstable_TracingMarker name="marker two">
616 + <AsyncText text="Inner Text Two" />
617 + </React.unstable_TracingMarker>
618 + </Suspense>
619 + </Suspense>
620 + </React.unstable_TracingMarker>
621 + ) : (
622 + <Text text="Page One" />
623 + )}
624 + </div>
625 + );
626 + }
627 +
628 + const root = ReactNoop.createRoot({transitionCallbacks});
629 + await act(async () => {
630 + root.render(<App />);
631 + ReactNoop.expire(1000);
632 + await advanceTimers(1000);
633 +
634 + expect(Scheduler).toFlushAndYield(['Page One']);
635 + });
636 +
637 + await act(async () => {
638 + startTransition(() => navigateToPageTwo(), {name: 'page transition'});
639 +
640 + ReactNoop.expire(1000);
641 + await advanceTimers(1000);
642 +
643 + expect(Scheduler).toFlushAndYield([
644 + 'Suspend [Outer Text]',
645 + 'Suspend [Inner Text One]',
646 + 'Inner One...',
647 + 'Suspend [Inner Text Two]',
648 + 'Inner Two...',
649 + 'Outer...',
650 + 'onTransitionStart(page transition, 1000)',
651 + ]);
652 +
653 + ReactNoop.expire(1000);
654 + await advanceTimers(1000);
655 + await resolveText('Inner Text Two');
656 + expect(Scheduler).toFlushAndYield([]);
657 +
658 + ReactNoop.expire(1000);
659 + await advanceTimers(1000);
660 + await resolveText('Outer Text');
661 + expect(Scheduler).toFlushAndYield([
662 + 'Outer Text',
663 + 'Suspend [Inner Text One]',
664 + 'Inner One...',
665 + 'Inner Text Two',
666 + 'onMarkerComplete(page transition, marker two, 1000, 4000)',
667 + ]);
668 +
669 + ReactNoop.expire(1000);
670 + await advanceTimers(1000);
671 + await resolveText('Inner Text One');
672 + expect(Scheduler).toFlushAndYield([
673 + 'Inner Text One',
674 + 'onMarkerComplete(page transition, marker one, 1000, 5000)',
675 + 'onMarkerComplete(page transition, outer marker, 1000, 5000)',
676 + 'onTransitionComplete(page transition, 1000, 5000)',
677 + ]);
678 + });
679 + });
680 +
681 + // @gate enableTransitionTracing
682 + it.skip('marker interaction cancelled when name changes', async () => {
683 + const transitionCallbacks = {
684 + onTransitionStart: (name, startTime) => {
685 + Scheduler.unstable_yieldValue(
686 + `onTransitionStart(${name}, ${startTime})`,
687 + );
688 + },
689 + onTransitionComplete: (name, startTime, endTime) => {
690 + Scheduler.unstable_yieldValue(
691 + `onTransitionComplete(${name}, ${startTime}, ${endTime})`,
692 + );
693 + },
694 + onMarkerComplete: (transitioName, markerName, startTime, endTime) => {
695 + Scheduler.unstable_yieldValue(
696 + `onMarkerComplete(${transitioName}, ${markerName}, ${startTime}, ${endTime})`,
697 + );
698 + },
699 + };
700 +
701 + let navigateToPageTwo;
702 + let setMarkerNameFn;
703 + function App() {
704 + const [navigate, setNavigate] = useState(false);
705 + navigateToPageTwo = () => {
706 + setNavigate(true);
707 + };
708 +
709 + const [markerName, setMarkerName] = useState('old marker');
710 + setMarkerNameFn = () => setMarkerName('new marker');
711 +
712 + return (
713 + <div>
714 + {navigate ? (
715 + <React.unstable_TracingMarker name={markerName}>
716 + <Suspense fallback={<Text text="Loading..." />}>
717 + <AsyncText text="Page Two" />
718 + </Suspense>
719 + </React.unstable_TracingMarker>
720 + ) : (
721 + <Text text="Page One" />
722 + )}
723 + </div>
724 + );
725 + }
726 +
727 + const root = ReactNoop.createRoot({transitionCallbacks});
728 + await act(async () => {
729 + root.render(<App />);
730 + ReactNoop.expire(1000);
731 + await advanceTimers(1000);
732 +
733 + expect(Scheduler).toFlushAndYield(['Page One']);
734 +
735 + startTransition(() => navigateToPageTwo(), {name: 'page transition'});
736 + expect(Scheduler).toFlushAndYield([
737 + 'Suspend [Page Two]',
738 + 'Loading...',
739 + 'onTransitionStart(page transition, 1000)',
740 + ]);
741 +
742 + ReactNoop.expire(1000);
743 + await advanceTimers(1000);
744 + setMarkerNameFn();
745 +
746 + expect(Scheduler).toFlushAndYield(['Suspend [Page Two]', 'Loading...']);
747 + ReactNoop.expire(1000);
748 + await advanceTimers(1000);
749 + resolveText('Page Two');
750 +
751 + // Marker complete is not called because the marker name changed
752 + expect(Scheduler).toFlushAndYield([
753 + 'Page Two',
754 + 'onTransitionComplete(page transition, 1000, 3000)',
755 + ]);
756 + });
757 + });
758 +
759 + // @gate enableTransitionTracing
760 + it.skip('marker changes to new interaction when name changes', async () => {
761 + const transitionCallbacks = {
762 + onTransitionStart: (name, startTime) => {
763 + Scheduler.unstable_yieldValue(
764 + `onTransitionStart(${name}, ${startTime})`,
765 + );
766 + },
767 + onTransitionComplete: (name, startTime, endTime) => {
768 + Scheduler.unstable_yieldValue(
769 + `onTransitionComplete(${name}, ${startTime}, ${endTime})`,
770 + );
771 + },
772 + onMarkerComplete: (transitioName, markerName, startTime, endTime) => {
773 + Scheduler.unstable_yieldValue(
774 + `onMarkerComplete(${transitioName}, ${markerName}, ${startTime}, ${endTime})`,
775 + );
776 + },
777 + };
778 +
779 + let navigateToPageTwo;
780 + let setMarkerNameFn;
781 + function App() {
782 + const [navigate, setNavigate] = useState(false);
783 + navigateToPageTwo = () => {
784 + setNavigate(true);
785 + };
786 +
787 + const [markerName, setMarkerName] = useState('old marker');
788 + setMarkerNameFn = () => setMarkerName('new marker');
789 +
790 + return (
791 + <div>
792 + {navigate ? (
793 + <React.unstable_TracingMarker name={markerName}>
794 + <Suspense fallback={<Text text="Loading..." />}>
795 + <AsyncText text="Page Two" />
796 + </Suspense>
797 + </React.unstable_TracingMarker>
798 + ) : (
799 + <Text text="Page One" />
800 + )}
801 + </div>
802 + );
803 + }
804 +
805 + const root = ReactNoop.createRoot({transitionCallbacks});
806 + await act(async () => {
807 + root.render(<App />);
808 + ReactNoop.expire(1000);
809 + await advanceTimers(1000);
810 +
811 + expect(Scheduler).toFlushAndYield(['Page One']);
812 +
813 + startTransition(() => navigateToPageTwo(), {name: 'page transition'});
814 + expect(Scheduler).toFlushAndYield([
815 + 'Suspend [Page Two]',
816 + 'Loading...',
817 + 'onTransitionStart(page transition, 1000)',
818 + ]);
819 +
820 + ReactNoop.expire(1000);
821 + await advanceTimers(1000);
822 + startTransition(() => setMarkerNameFn(), {name: 'marker transition'});
823 +
824 + expect(Scheduler).toFlushAndYield([
825 + 'Suspend [Page Two]',
826 + 'Loading...',
827 + 'onTransitionStart(marker transition, 2000)',
828 + ]);
829 + ReactNoop.expire(1000);
830 + await advanceTimers(1000);
831 + resolveText('Page Two');
832 +
833 + // Marker complete is not called because the marker name changed
834 + expect(Scheduler).toFlushAndYield([
835 + 'Page Two',
836 + 'onMarkerComplete(new marker, 2000, 3000)',
837 + 'onTransitionComplete(page transition, 1000, 3000)',
838 + ]);
839 + });
840 + });
841 });