Don't call onCommit et al if there are no effects (#19863)
* Don't call onCommit et al if there are no effects Checks `subtreeFlags` before scheduling an effect on the Profiler. * Fix failing Profiler tests The change to conditionally call Profiler commit hooks only if updates were scheduled broke a few of the Profiler tests. I've fixed the tests by either: * Adding a no-op passive effect into the subtree or * Converting onPostCommit to onCommit When possible, I opted to add the no-op passive effect to the tests since that that hook is called later (during passive phase) so the test is a little broader. In a few cases, this required adding awkward act() wrappers so I opted to go with onCommit instead. Co-authored-by: Brian Vaughn <bvaughn@fb.com>
Andrew Clark committed
Sep 22, 2020 at 13:20 UTC
81aaee56afba2bb3558f2aaa484b594f23b59d4c
4 files changed
+795
-714
packages/react-reconciler/src/ReactFiberBeginWork.new.js
-15
@@ -60,8 +60,6 @@ import {
60
Hydrating,
61
ContentReset,
62
DidCapture,
63
- Update,
64
- Passive,
63
Ref,
64
Deletion,
65
ForceUpdateForLegacySuspense,
@@ -675,9 +673,6 @@ function updateProfiler(
673
renderLanes: Lanes,
674
) {
675
if (enableProfilerTimer) {
678
- // TODO: Only call onRender et al if subtree has effects
679
- workInProgress.flags |= Update | Passive;
680
-
676
// Reset effect durations for the next eventual effect phase.
677
// These are reset during render to allow the DevTools commit hook a chance to read them,
678
const stateNode = workInProgress.stateNode;
@@ -3117,16 +3112,6 @@ function beginWork(
3112
}
3113
case Profiler:
3114
if (enableProfilerTimer) {
3120
- // Profiler should only call onRender when one of its descendants actually rendered.
3121
- // TODO: Only call onRender et al if subtree has effects
3122
- const hasChildWork = includesSomeLane(
3123
- renderLanes,
3124
- workInProgress.childLanes,
3125
- );
3126
- if (hasChildWork) {
3127
- workInProgress.flags |= Passive | Update;
3128
- }
3129
-
3115
// Reset effect durations for the next eventual effect phase.
3116
// These are reset during render to allow the DevTools commit hook a chance to read them,
3117
const stateNode = workInProgress.stateNode;
packages/react-reconciler/src/ReactFiberCommitWork.new.js
+13
-2
@@ -68,6 +68,7 @@ import {
68
Placement,
69
Snapshot,
70
Update,
71
+ Callback,
72
PassiveMask,
73
} from './ReactFiberFlags';
74
import getComponentName from 'shared/getComponentName';
@@ -676,10 +677,17 @@ function commitLifeCycles(
677
if (enableProfilerTimer) {
678
const {onCommit, onRender} = finishedWork.memoizedProps;
679
const {effectDuration} = finishedWork.stateNode;
680
+ const flags = finishedWork.flags;
681
682
const commitTime = getCommitTime();
683
682
- if (typeof onRender === 'function') {
684
+ const OnRenderFlag = Update;
685
+ const OnCommitFlag = Callback;
686
+
687
+ if (
688
+ (flags & OnRenderFlag) !== NoFlags &&
689
+ typeof onRender === 'function'
690
+ ) {
691
if (enableSchedulerTracing) {
692
onRender(
693
finishedWork.memoizedProps.id,
@@ -703,7 +711,10 @@ function commitLifeCycles(
711
}
712
713
if (enableProfilerCommitHooks) {
706
- if (typeof onCommit === 'function') {
714
+ if (
715
+ (flags & OnCommitFlag) !== NoFlags &&
716
+ typeof onCommit === 'function'
717
+ ) {
718
if (enableSchedulerTracing) {
719
onCommit(
720
finishedWork.memoizedProps.id,
packages/react-reconciler/src/ReactFiberCompleteWork.new.js
+54
-1
@@ -67,10 +67,15 @@ import {
67
import {
68
Ref,
69
Update,
70
+ Callback,
71
+ Passive,
72
+ Deletion,
73
NoFlags,
74
DidCapture,
75
Snapshot,
76
MutationMask,
77
+ LayoutMask,
78
+ PassiveMask,
79
StaticMask,
80
} from './ReactFiberFlags';
81
import invariant from 'shared/invariant';
@@ -787,6 +792,8 @@ function bubbleProperties(completedWork: Fiber) {
792
}
793
794
completedWork.childLanes = newChildLanes;
795
+
796
+ return didBailout;
797
}
798
799
function completeWork(
@@ -804,7 +811,6 @@ function completeWork(
811
case ForwardRef:
812
case Fragment:
813
case Mode:
807
- case Profiler:
814
case ContextConsumer:
815
case MemoComponent:
816
bubbleProperties(workInProgress);
@@ -966,6 +972,53 @@ function completeWork(
972
bubbleProperties(workInProgress);
973
return null;
974
}
975
+ case Profiler: {
976
+ const didBailout = bubbleProperties(workInProgress);
977
+ if (!didBailout) {
978
+ // Use subtreeFlags to determine which commit callbacks should fire.
979
+ // TODO: Move this logic to the commit phase, since we already check if
980
+ // a fiber's subtree contains effects. Refactor the commit phase's
981
+ // depth-first traversal so that we can put work tag-specific logic
982
+ // before or after committing a subtree's effects.
983
+ const OnRenderFlag = Update;
984
+ const OnCommitFlag = Callback;
985
+ const OnPostCommitFlag = Passive;
986
+ const subtreeFlags = workInProgress.subtreeFlags;
987
+ const flags = workInProgress.flags;
988
+ let newFlags = flags;
989
+
990
+ // Call onRender any time this fiber or its subtree are worked on, even
991
+ // if there are no effects
992
+ newFlags |= OnRenderFlag;
993
+
994
+ // Call onCommit only if the subtree contains layout work, or if it
995
+ // contains deletions, since those might result in unmount work, which
996
+ // we include in the same measure.
997
+ // TODO: Can optimize by using a static flag to track whether a tree
998
+ // contains layout effects, like we do for passive effects.
999
+ if (
1000
+ (flags & (LayoutMask | Deletion)) !== NoFlags ||
1001
+ (subtreeFlags & (LayoutMask | Deletion)) !== NoFlags
1002
+ ) {
1003
+ newFlags |= OnCommitFlag;
1004
+ }
1005
+
1006
+ // Call onPostCommit only if the subtree contains passive work.
1007
+ // Don't have to check for deletions, because Deletion is already
1008
+ // a passive flag.
1009
+ if (
1010
+ (flags & PassiveMask) !== NoFlags ||
1011
+ (subtreeFlags & PassiveMask) !== NoFlags
1012
+ ) {
1013
+ newFlags |= OnPostCommitFlag;
1014
+ }
1015
+ workInProgress.flags = newFlags;
1016
+ } else {
1017
+ // This fiber and its subtree bailed out, so don't fire any callbacks.
1018
+ }
1019
+
1020
+ return null;
1021
+ }
1022
case SuspenseComponent: {
1023
popSuspenseContext(workInProgress);
1024
const nextState: null | SuspenseState = workInProgress.memoizedState;
packages/react/src/__tests__/ReactProfiler-test.internal.js
+728
-696
@@ -20,6 +20,7 @@ let ReactTestRendererAct;
20
let SchedulerTracing;
21
let AdvanceTime;
22
let AsyncText;
23
+let ComponentWithPassiveEffect;
24
let Text;
25
let TextResource;
26
let resourcePromise;
@@ -102,6 +103,12 @@ function loadModules({
103
Scheduler.unstable_yieldValue(`Text [${text}]`);
104
return text;
105
};
106
+
107
+ ComponentWithPassiveEffect = () => {
108
+ // Intentionally schedule a passive effect so the onPostCommit hook will be called.
109
+ React.useEffect(() => {});
110
+ return null;
111
+ };
112
}
113
114
describe('Profiler', () => {
@@ -2622,251 +2629,256 @@ describe('Profiler', () => {
2629
<React.Fragment>
2630
<Yield value="first" />
2631
{this.state.count}
2632
+ <ComponentWithPassiveEffect count={this.state.count} />
2633
<Yield value="last" />
2634
</React.Fragment>
2635
);
2636
}
2637
}
2638
2631
- Scheduler.unstable_advanceTime(1);
2632
-
2633
- const interactionCreation = {
2634
- id: 0,
2635
- name: 'creation event',
2636
- timestamp: Scheduler.unstable_now(),
2637
- };
2639
+ ReactTestRendererAct(() => {
2640
+ Scheduler.unstable_advanceTime(1);
2641
2639
- const onPostCommit = jest.fn(() => {
2640
- Scheduler.unstable_yieldValue('onPostCommit');
2641
- });
2642
- let renderer;
2643
- SchedulerTracing.unstable_trace(
2644
- interactionCreation.name,
2645
- Scheduler.unstable_now(),
2646
- () => {
2647
- renderer = ReactTestRenderer.create(
2648
- <React.Profiler id="test-profiler" onPostCommit={onPostCommit}>
2649
- <Example />
2650
- </React.Profiler>,
2651
- {
2652
- unstable_isConcurrent: true,
2653
- },
2654
- );
2655
- },
2656
- );
2642
+ const interactionCreation = {
2643
+ id: 0,
2644
+ name: 'creation event',
2645
+ timestamp: Scheduler.unstable_now(),
2646
+ };
2647
2658
- expect(onInteractionTraced).toHaveBeenCalledTimes(1);
2659
- expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
2660
- interactionCreation,
2661
- );
2662
- expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
2648
+ const onPostCommit = jest.fn(() => {
2649
+ Scheduler.unstable_yieldValue('onPostCommit');
2650
+ });
2651
+ let renderer;
2652
+ SchedulerTracing.unstable_trace(
2653
+ interactionCreation.name,
2654
+ Scheduler.unstable_now(),
2655
+ () => {
2656
+ renderer = ReactTestRenderer.create(
2657
+ <React.Profiler id="test-profiler" onPostCommit={onPostCommit}>
2658
+ <Example />
2659
+ </React.Profiler>,
2660
+ {
2661
+ unstable_isConcurrent: true,
2662
+ },
2663
+ );
2664
+ },
2665
+ );
2666
2664
- // The scheduler/tracing package will notify of work started for the default thread,
2665
- // But React shouldn't notify until it's been flushed.
2666
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
2667
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
2668
-
2669
- // Work may have been scheduled multiple times.
2670
- // We only care that the subscriber was notified at least once.
2671
- // As for the thread ID- the actual value isn't important, only that there was one.
2672
- expect(onWorkScheduled).toHaveBeenCalled();
2673
- expect(onWorkScheduled.mock.calls[0][0]).toMatchInteractions([
2674
- interactionCreation,
2675
- ]);
2676
- expect(onWorkScheduled.mock.calls[0][1] > 0).toBe(true);
2677
-
2678
- // Mount
2679
- expect(Scheduler).toFlushAndYield(['first', 'last', 'onPostCommit']);
2680
- expect(onPostCommit).toHaveBeenCalledTimes(1);
2681
- let call = onPostCommit.mock.calls[0];
2682
- expect(call[0]).toEqual('test-profiler');
2683
- expect(call[3]).toEqual(Scheduler.unstable_now());
2684
- if (ReactFeatureFlags.enableSchedulerTracing) {
2685
- expect(call[4]).toMatchInteractions([interactionCreation]);
2686
- }
2667
+ expect(onInteractionTraced).toHaveBeenCalledTimes(1);
2668
+ expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
2669
+ interactionCreation,
2670
+ );
2671
+ expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
2672
2688
- expect(onInteractionTraced).toHaveBeenCalledTimes(1);
2689
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
2690
- expect(
2691
- onInteractionScheduledWorkCompleted,
2692
- ).toHaveBeenLastNotifiedOfInteraction(interactionCreation);
2693
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
2694
- expect(getWorkForReactThreads(onWorkStarted)[0][0]).toMatchInteractions([
2695
- interactionCreation,
2696
- ]);
2697
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(1);
2698
- expect(getWorkForReactThreads(onWorkStopped)[0][0]).toMatchInteractions([
2699
- interactionCreation,
2700
- ]);
2673
+ // The scheduler/tracing package will notify of work started for the default thread,
2674
+ // But React shouldn't notify until it's been flushed.
2675
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
2676
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
2677
2702
- onPostCommit.mockClear();
2703
- onWorkScheduled.mockClear();
2704
- onWorkStarted.mockClear();
2705
- onWorkStopped.mockClear();
2678
+ // Work may have been scheduled multiple times.
2679
+ // We only care that the subscriber was notified at least once.
2680
+ // As for the thread ID- the actual value isn't important, only that there was one.
2681
+ expect(onWorkScheduled).toHaveBeenCalled();
2682
+ expect(onWorkScheduled.mock.calls[0][0]).toMatchInteractions([
2683
+ interactionCreation,
2684
+ ]);
2685
+ expect(onWorkScheduled.mock.calls[0][1] > 0).toBe(true);
2686
2707
- Scheduler.unstable_advanceTime(3);
2687
+ // Mount
2688
+ expect(Scheduler).toFlushAndYield(['first', 'last', 'onPostCommit']);
2689
+ expect(onPostCommit).toHaveBeenCalledTimes(1);
2690
+ let call = onPostCommit.mock.calls[0];
2691
+ expect(call[0]).toEqual('test-profiler');
2692
+ expect(call[3]).toEqual(Scheduler.unstable_now());
2693
+ if (ReactFeatureFlags.enableSchedulerTracing) {
2694
+ expect(call[4]).toMatchInteractions([interactionCreation]);
2695
+ }
2696
2709
- let didRunCallback = false;
2697
+ expect(onInteractionTraced).toHaveBeenCalledTimes(1);
2698
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
2699
+ expect(
2700
+ onInteractionScheduledWorkCompleted,
2701
+ ).toHaveBeenLastNotifiedOfInteraction(interactionCreation);
2702
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
2703
+ expect(
2704
+ getWorkForReactThreads(onWorkStarted)[0][0],
2705
+ ).toMatchInteractions([interactionCreation]);
2706
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(1);
2707
+ expect(
2708
+ getWorkForReactThreads(onWorkStopped)[0][0],
2709
+ ).toMatchInteractions([interactionCreation]);
2710
2711
- const interactionOne = {
2712
- id: 1,
2713
- name: 'initial event',
2714
- timestamp: Scheduler.unstable_now(),
2715
- };
2716
- SchedulerTracing.unstable_trace(
2717
- interactionOne.name,
2718
- Scheduler.unstable_now(),
2719
- () => {
2720
- instance.setState({count: 1});
2711
+ onPostCommit.mockClear();
2712
+ onWorkScheduled.mockClear();
2713
+ onWorkStarted.mockClear();
2714
+ onWorkStopped.mockClear();
2715
2722
- // Update state again to verify our traced interaction isn't registered twice
2723
- instance.setState({count: 2});
2716
+ Scheduler.unstable_advanceTime(3);
2717
2725
- // The scheduler/tracing package will notify of work started for the default thread,
2726
- // But React shouldn't notify until it's been flushed.
2727
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
2728
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
2718
+ let didRunCallback = false;
2719
2730
- // Work may have been scheduled multiple times.
2731
- // We only care that the subscriber was notified at least once.
2732
- // As for the thread ID- the actual value isn't important, only that there was one.
2733
- expect(onWorkScheduled).toHaveBeenCalled();
2734
- expect(onWorkScheduled.mock.calls[0][0]).toMatchInteractions([
2735
- interactionOne,
2736
- ]);
2737
- expect(onWorkScheduled.mock.calls[0][1] > 0).toBe(true);
2720
+ const interactionOne = {
2721
+ id: 1,
2722
+ name: 'initial event',
2723
+ timestamp: Scheduler.unstable_now(),
2724
+ };
2725
+ SchedulerTracing.unstable_trace(
2726
+ interactionOne.name,
2727
+ Scheduler.unstable_now(),
2728
+ () => {
2729
+ instance.setState({count: 1});
2730
+
2731
+ // Update state again to verify our traced interaction isn't registered twice
2732
+ instance.setState({count: 2});
2733
+
2734
+ // The scheduler/tracing package will notify of work started for the default thread,
2735
+ // But React shouldn't notify until it's been flushed.
2736
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
2737
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
2738
+
2739
+ // Work may have been scheduled multiple times.
2740
+ // We only care that the subscriber was notified at least once.
2741
+ // As for the thread ID- the actual value isn't important, only that there was one.
2742
+ expect(onWorkScheduled).toHaveBeenCalled();
2743
+ expect(onWorkScheduled.mock.calls[0][0]).toMatchInteractions([
2744
+ interactionOne,
2745
+ ]);
2746
+ expect(onWorkScheduled.mock.calls[0][1] > 0).toBe(true);
2747
+
2748
+ expect(Scheduler).toFlushAndYieldThrough(['first']);
2749
+ expect(onPostCommit).not.toHaveBeenCalled();
2750
+
2751
+ expect(onInteractionTraced).toHaveBeenCalledTimes(2);
2752
+ expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
2753
+ interactionOne,
2754
+ );
2755
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(
2756
+ 1,
2757
+ );
2758
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
2759
+ expect(
2760
+ getWorkForReactThreads(onWorkStarted)[0][0],
2761
+ ).toMatchInteractions([interactionOne]);
2762
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
2763
+
2764
+ expect(Scheduler).toFlushAndYield(['last', 'onPostCommit']);
2765
+ expect(onPostCommit).toHaveBeenCalledTimes(1);
2766
+
2767
+ call = onPostCommit.mock.calls[0];
2768
+ expect(call[0]).toEqual('test-profiler');
2769
+ expect(call[3]).toEqual(Scheduler.unstable_now());
2770
+ if (ReactFeatureFlags.enableSchedulerTracing) {
2771
+ expect(call[4]).toMatchInteractions([interactionOne]);
2772
+ }
2773
2739
- expect(Scheduler).toFlushAndYieldThrough(['first']);
2740
- expect(onPostCommit).not.toHaveBeenCalled();
2774
+ didRunCallback = true;
2775
2742
- expect(onInteractionTraced).toHaveBeenCalledTimes(2);
2743
- expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
2744
- interactionOne,
2745
- );
2746
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
2747
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
2748
- expect(
2749
- getWorkForReactThreads(onWorkStarted)[0][0],
2750
- ).toMatchInteractions([interactionOne]);
2751
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
2776
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
2777
+ expect(
2778
+ getWorkForReactThreads(onWorkStarted)[0][0],
2779
+ ).toMatchInteractions([interactionOne]);
2780
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(1);
2781
+ expect(
2782
+ getWorkForReactThreads(onWorkStopped)[0][0],
2783
+ ).toMatchInteractions([interactionOne]);
2784
+ },
2785
+ );
2786
2753
- expect(Scheduler).toFlushAndYield(['last', 'onPostCommit']);
2754
- expect(onPostCommit).toHaveBeenCalledTimes(1);
2787
+ expect(didRunCallback).toBe(true);
2788
2756
- call = onPostCommit.mock.calls[0];
2757
- expect(call[0]).toEqual('test-profiler');
2758
- expect(call[3]).toEqual(Scheduler.unstable_now());
2759
- if (ReactFeatureFlags.enableSchedulerTracing) {
2760
- expect(call[4]).toMatchInteractions([interactionOne]);
2761
- }
2789
+ onPostCommit.mockClear();
2790
+ onWorkScheduled.mockClear();
2791
+ onWorkStarted.mockClear();
2792
+ onWorkStopped.mockClear();
2793
2763
- didRunCallback = true;
2794
+ Scheduler.unstable_advanceTime(17);
2795
2765
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
2766
- expect(
2767
- getWorkForReactThreads(onWorkStarted)[0][0],
2768
- ).toMatchInteractions([interactionOne]);
2769
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(1);
2770
- expect(
2771
- getWorkForReactThreads(onWorkStopped)[0][0],
2772
- ).toMatchInteractions([interactionOne]);
2773
- },
2774
- );
2796
+ // Verify that updating state again does not re-log our interaction.
2797
+ instance.setState({count: 3});
2798
+ expect(Scheduler).toFlushAndYield(['first', 'last', 'onPostCommit']);
2799
2776
- expect(didRunCallback).toBe(true);
2800
+ expect(onPostCommit).toHaveBeenCalledTimes(1);
2801
+ call = onPostCommit.mock.calls[0];
2802
+ expect(call[0]).toEqual('test-profiler');
2803
+ expect(call[3]).toEqual(Scheduler.unstable_now());
2804
+ if (ReactFeatureFlags.enableSchedulerTracing) {
2805
+ expect(call[4]).toMatchInteractions([]);
2806
+ }
2807
2778
- onPostCommit.mockClear();
2779
- onWorkScheduled.mockClear();
2780
- onWorkStarted.mockClear();
2781
- onWorkStopped.mockClear();
2808
+ expect(onInteractionTraced).toHaveBeenCalledTimes(2);
2809
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
2810
+ expect(
2811
+ onInteractionScheduledWorkCompleted,
2812
+ ).toHaveBeenLastNotifiedOfInteraction(interactionOne);
2813
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
2814
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
2815
2783
- Scheduler.unstable_advanceTime(17);
2816
+ onPostCommit.mockClear();
2817
2785
- // Verify that updating state again does not re-log our interaction.
2786
- instance.setState({count: 3});
2787
- expect(Scheduler).toFlushAndYield(['first', 'last', 'onPostCommit']);
2818
+ Scheduler.unstable_advanceTime(3);
2819
2789
- expect(onPostCommit).toHaveBeenCalledTimes(1);
2790
- call = onPostCommit.mock.calls[0];
2791
- expect(call[0]).toEqual('test-profiler');
2792
- expect(call[3]).toEqual(Scheduler.unstable_now());
2793
- if (ReactFeatureFlags.enableSchedulerTracing) {
2794
- expect(call[4]).toMatchInteractions([]);
2795
- }
2820
+ // Verify that root updates are also associated with traced events.
2821
+ const interactionTwo = {
2822
+ id: 2,
2823
+ name: 'root update event',
2824
+ timestamp: Scheduler.unstable_now(),
2825
+ };
2826
+ SchedulerTracing.unstable_trace(
2827
+ interactionTwo.name,
2828
+ Scheduler.unstable_now(),
2829
+ () => {
2830
+ renderer.update(
2831
+ <React.Profiler id="test-profiler" onPostCommit={onPostCommit}>
2832
+ <Example />
2833
+ </React.Profiler>,
2834
+ );
2835
+ },
2836
+ );
2837
2797
- expect(onInteractionTraced).toHaveBeenCalledTimes(2);
2798
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
2799
- expect(
2800
- onInteractionScheduledWorkCompleted,
2801
- ).toHaveBeenLastNotifiedOfInteraction(interactionOne);
2802
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
2803
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
2838
+ expect(onInteractionTraced).toHaveBeenCalledTimes(3);
2839
+ expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
2840
+ interactionTwo,
2841
+ );
2842
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
2843
2805
- onPostCommit.mockClear();
2844
+ // The scheduler/tracing package will notify of work started for the default thread,
2845
+ // But React shouldn't notify until it's been flushed.
2846
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
2847
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
2848
2807
- Scheduler.unstable_advanceTime(3);
2849
+ // Work may have been scheduled multiple times.
2850
+ // We only care that the subscriber was notified at least once.
2851
+ // As for the thread ID- the actual value isn't important, only that there was one.
2852
+ expect(onWorkScheduled).toHaveBeenCalled();
2853
+ expect(onWorkScheduled.mock.calls[0][0]).toMatchInteractions([
2854
+ interactionTwo,
2855
+ ]);
2856
+ expect(onWorkScheduled.mock.calls[0][1] > 0).toBe(true);
2857
2809
- // Verify that root updates are also associated with traced events.
2810
- const interactionTwo = {
2811
- id: 2,
2812
- name: 'root update event',
2813
- timestamp: Scheduler.unstable_now(),
2814
- };
2815
- SchedulerTracing.unstable_trace(
2816
- interactionTwo.name,
2817
- Scheduler.unstable_now(),
2818
- () => {
2819
- renderer.update(
2820
- <React.Profiler id="test-profiler" onPostCommit={onPostCommit}>
2821
- <Example />
2822
- </React.Profiler>,
2823
- );
2824
- },
2825
- );
2858
+ expect(Scheduler).toFlushAndYield(['first', 'last', 'onPostCommit']);
2859
2827
- expect(onInteractionTraced).toHaveBeenCalledTimes(3);
2828
- expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
2829
- interactionTwo,
2830
- );
2831
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
2832
-
2833
- // The scheduler/tracing package will notify of work started for the default thread,
2834
- // But React shouldn't notify until it's been flushed.
2835
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
2836
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
2837
-
2838
- // Work may have been scheduled multiple times.
2839
- // We only care that the subscriber was notified at least once.
2840
- // As for the thread ID- the actual value isn't important, only that there was one.
2841
- expect(onWorkScheduled).toHaveBeenCalled();
2842
- expect(onWorkScheduled.mock.calls[0][0]).toMatchInteractions([
2843
- interactionTwo,
2844
- ]);
2845
- expect(onWorkScheduled.mock.calls[0][1] > 0).toBe(true);
2846
-
2847
- expect(Scheduler).toFlushAndYield(['first', 'last', 'onPostCommit']);
2848
-
2849
- expect(onPostCommit).toHaveBeenCalledTimes(1);
2850
- call = onPostCommit.mock.calls[0];
2851
- expect(call[0]).toEqual('test-profiler');
2852
- expect(call[3]).toEqual(Scheduler.unstable_now());
2853
- if (ReactFeatureFlags.enableSchedulerTracing) {
2854
- expect(call[4]).toMatchInteractions([interactionTwo]);
2855
- }
2860
+ expect(onPostCommit).toHaveBeenCalledTimes(1);
2861
+ call = onPostCommit.mock.calls[0];
2862
+ expect(call[0]).toEqual('test-profiler');
2863
+ expect(call[3]).toEqual(Scheduler.unstable_now());
2864
+ if (ReactFeatureFlags.enableSchedulerTracing) {
2865
+ expect(call[4]).toMatchInteractions([interactionTwo]);
2866
+ }
2867
2857
- expect(onInteractionTraced).toHaveBeenCalledTimes(3);
2858
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(3);
2859
- expect(
2860
- onInteractionScheduledWorkCompleted,
2861
- ).toHaveBeenLastNotifiedOfInteraction(interactionTwo);
2862
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
2863
- expect(getWorkForReactThreads(onWorkStarted)[0][0]).toMatchInteractions([
2864
- interactionTwo,
2865
- ]);
2866
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(1);
2867
- expect(getWorkForReactThreads(onWorkStopped)[0][0]).toMatchInteractions([
2868
- interactionTwo,
2869
- ]);
2868
+ expect(onInteractionTraced).toHaveBeenCalledTimes(3);
2869
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(3);
2870
+ expect(
2871
+ onInteractionScheduledWorkCompleted,
2872
+ ).toHaveBeenLastNotifiedOfInteraction(interactionTwo);
2873
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
2874
+ expect(
2875
+ getWorkForReactThreads(onWorkStarted)[0][0],
2876
+ ).toMatchInteractions([interactionTwo]);
2877
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(1);
2878
+ expect(
2879
+ getWorkForReactThreads(onWorkStopped)[0][0],
2880
+ ).toMatchInteractions([interactionTwo]);
2881
+ });
2882
});
2883
2884
it('should not mark an interaction complete while passive effects are outstanding', () => {
@@ -2921,7 +2933,7 @@ describe('Profiler', () => {
2933
render() {
2934
first = this;
2935
Scheduler.unstable_yieldValue('FirstComponent');
2924
- return null;
2936
+ return <ComponentWithPassiveEffect count={this.state.count} />;
2937
}
2938
}
2939
let second;
@@ -2930,165 +2942,176 @@ describe('Profiler', () => {
2942
render() {
2943
second = this;
2944
Scheduler.unstable_yieldValue('SecondComponent');
2933
- return null;
2945
+ return <ComponentWithPassiveEffect count={this.state.count} />;
2946
}
2947
}
2948
2949
Scheduler.unstable_advanceTime(5);
2950
2939
- const renderer = ReactTestRenderer.create(
2940
- <React.Profiler id="test" onPostCommit={onPostCommit}>
2941
- <FirstComponent />
2942
- <SecondComponent />
2943
- </React.Profiler>,
2944
- {unstable_isConcurrent: true},
2945
- );
2946
-
2947
- // Initial mount.
2948
- expect(Scheduler).toFlushAndYield([
2949
- 'FirstComponent',
2950
- 'SecondComponent',
2951
- 'onPostCommit',
2952
- ]);
2953
-
2954
- expect(onInteractionTraced).not.toHaveBeenCalled();
2955
- expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
2956
-
2957
- onPostCommit.mockClear();
2958
-
2959
- Scheduler.unstable_advanceTime(100);
2960
-
2961
- const interactionLowPri = {
2962
- id: 0,
2963
- name: 'lowPri',
2964
- timestamp: Scheduler.unstable_now(),
2965
- };
2951
+ ReactTestRendererAct(() => {
2952
+ const renderer = ReactTestRenderer.create(
2953
+ <React.Profiler id="test" onPostCommit={onPostCommit}>
2954
+ <FirstComponent />
2955
+ <SecondComponent />
2956
+ </React.Profiler>,
2957
+ {unstable_isConcurrent: true},
2958
+ );
2959
2967
- SchedulerTracing.unstable_trace(
2968
- interactionLowPri.name,
2969
- Scheduler.unstable_now(),
2970
- () => {
2971
- // Render a partially update, but don't finish.
2972
- first.setState({count: 1});
2960
+ // Initial mount.
2961
+ expect(Scheduler).toFlushAndYield([
2962
+ 'FirstComponent',
2963
+ 'SecondComponent',
2964
+ 'onPostCommit',
2965
+ ]);
2966
2974
- expect(onWorkScheduled).toHaveBeenCalled();
2975
- expect(onWorkScheduled.mock.calls[0][0]).toMatchInteractions([
2976
- interactionLowPri,
2977
- ]);
2967
+ expect(onInteractionTraced).not.toHaveBeenCalled();
2968
+ expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
2969
2979
- expect(Scheduler).toFlushAndYieldThrough(['FirstComponent']);
2980
- expect(onPostCommit).not.toHaveBeenCalled();
2970
+ onPostCommit.mockClear();
2971
2982
- expect(onInteractionTraced).toHaveBeenCalledTimes(1);
2983
- expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
2984
- interactionLowPri,
2985
- );
2986
- expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
2987
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
2988
- expect(
2989
- getWorkForReactThreads(onWorkStarted)[0][0],
2990
- ).toMatchInteractions([interactionLowPri]);
2991
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
2972
+ Scheduler.unstable_advanceTime(100);
2973
2993
- Scheduler.unstable_advanceTime(100);
2974
+ const interactionLowPri = {
2975
+ id: 0,
2976
+ name: 'lowPri',
2977
+ timestamp: Scheduler.unstable_now(),
2978
+ };
2979
2995
- const interactionHighPri = {
2996
- id: 1,
2997
- name: 'highPri',
2998
- timestamp: Scheduler.unstable_now(),
2999
- };
2980
+ SchedulerTracing.unstable_trace(
2981
+ interactionLowPri.name,
2982
+ Scheduler.unstable_now(),
2983
+ () => {
2984
+ // Render a partially update, but don't finish.
2985
+ first.setState({count: 1});
2986
3001
- // Interrupt with higher priority work.
3002
- // This simulates a total of 37ms of actual render time.
3003
- renderer.unstable_flushSync(() => {
3004
- SchedulerTracing.unstable_trace(
3005
- interactionHighPri.name,
3006
- Scheduler.unstable_now(),
3007
- () => {
3008
- second.setState({count: 1});
2987
+ expect(onWorkScheduled).toHaveBeenCalled();
2988
+ expect(onWorkScheduled.mock.calls[0][0]).toMatchInteractions([
2989
+ interactionLowPri,
2990
+ ]);
2991
3010
- expect(onInteractionTraced).toHaveBeenCalledTimes(2);
3011
- expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
3012
- interactionHighPri,
3013
- );
3014
- expect(
3015
- onInteractionScheduledWorkCompleted,
3016
- ).not.toHaveBeenCalled();
2992
+ expect(Scheduler).toFlushAndYieldThrough(['FirstComponent']);
2993
+ expect(onPostCommit).not.toHaveBeenCalled();
2994
3018
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
3019
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
3020
- },
2995
+ expect(onInteractionTraced).toHaveBeenCalledTimes(1);
2996
+ expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
2997
+ interactionLowPri,
2998
);
3022
- });
2999
+ expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
3000
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
3001
+ expect(
3002
+ getWorkForReactThreads(onWorkStarted)[0][0],
3003
+ ).toMatchInteractions([interactionLowPri]);
3004
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
3005
3024
- // Profiler tag causes passive effects to be scheduled,
3025
- // so the interactions are still not completed.
3026
- expect(Scheduler).toHaveYielded(['SecondComponent']);
3027
- expect(onInteractionTraced).toHaveBeenCalledTimes(2);
3028
- expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
3029
- expect(Scheduler).toFlushAndYieldThrough(['onPostCommit']);
3006
+ Scheduler.unstable_advanceTime(100);
3007
3031
- expect(onInteractionTraced).toHaveBeenCalledTimes(2);
3032
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
3033
- expect(
3034
- onInteractionScheduledWorkCompleted,
3035
- ).toHaveBeenLastNotifiedOfInteraction(interactionHighPri);
3008
+ const interactionHighPri = {
3009
+ id: 1,
3010
+ name: 'highPri',
3011
+ timestamp: Scheduler.unstable_now(),
3012
+ };
3013
3037
- // Verify the high priority update was associated with the high priority event.
3038
- expect(onPostCommit).toHaveBeenCalledTimes(1);
3039
- let call = onPostCommit.mock.calls[0];
3040
- expect(call[0]).toEqual('test');
3041
- expect(call[3]).toEqual(Scheduler.unstable_now());
3042
- expect(call[4]).toMatchInteractions(
3043
- ReactFeatureFlags.enableSchedulerTracing
3044
- ? [interactionLowPri, interactionHighPri]
3045
- : [],
3046
- );
3014
+ // Interrupt with higher priority work.
3015
+ // This simulates a total of 37ms of actual render time.
3016
+ renderer.unstable_flushSync(() => {
3017
+ SchedulerTracing.unstable_trace(
3018
+ interactionHighPri.name,
3019
+ Scheduler.unstable_now(),
3020
+ () => {
3021
+ second.setState({count: 1});
3022
+
3023
+ expect(onInteractionTraced).toHaveBeenCalledTimes(2);
3024
+ expect(
3025
+ onInteractionTraced,
3026
+ ).toHaveBeenLastNotifiedOfInteraction(interactionHighPri);
3027
+ expect(
3028
+ onInteractionScheduledWorkCompleted,
3029
+ ).not.toHaveBeenCalled();
3030
+
3031
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
3032
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
3033
+ },
3034
+ );
3035
+ });
3036
3048
- onPostCommit.mockClear();
3037
+ // Profiler tag causes passive effects to be scheduled,
3038
+ // so the interactions are still not completed.
3039
+ expect(Scheduler).toHaveYielded(['SecondComponent']);
3040
+ expect(onInteractionTraced).toHaveBeenCalledTimes(2);
3041
+ expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
3042
+ expect(Scheduler).toFlushAndYieldThrough(['onPostCommit']);
3043
3050
- Scheduler.unstable_advanceTime(100);
3044
+ expect(onInteractionTraced).toHaveBeenCalledTimes(2);
3045
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(
3046
+ 1,
3047
+ );
3048
+ expect(
3049
+ onInteractionScheduledWorkCompleted,
3050
+ ).toHaveBeenLastNotifiedOfInteraction(interactionHighPri);
3051
+
3052
+ // Verify the high priority update was associated with the high priority event.
3053
+ expect(onPostCommit).toHaveBeenCalledTimes(1);
3054
+ let call = onPostCommit.mock.calls[0];
3055
+ expect(call[0]).toEqual('test');
3056
+ expect(call[3]).toEqual(Scheduler.unstable_now());
3057
+ expect(call[4]).toMatchInteractions(
3058
+ ReactFeatureFlags.enableSchedulerTracing
3059
+ ? [interactionLowPri, interactionHighPri]
3060
+ : [],
3061
+ );
3062
3052
- // Resume the original low priority update, with rebased state.
3053
- // Verify the low priority update was retained.
3054
- expect(Scheduler).toFlushAndYield(['FirstComponent', 'onPostCommit']);
3055
- expect(onPostCommit).toHaveBeenCalledTimes(1);
3056
- call = onPostCommit.mock.calls[0];
3057
- expect(call[0]).toEqual('test');
3058
- expect(call[3]).toEqual(Scheduler.unstable_now());
3059
- expect(call[4]).toMatchInteractions(
3060
- ReactFeatureFlags.enableSchedulerTracing ? [interactionLowPri] : [],
3061
- );
3063
+ onPostCommit.mockClear();
3064
+
3065
+ Scheduler.unstable_advanceTime(100);
3066
+
3067
+ // Resume the original low priority update, with rebased state.
3068
+ // Verify the low priority update was retained.
3069
+ expect(Scheduler).toFlushAndYield([
3070
+ 'FirstComponent',
3071
+ 'onPostCommit',
3072
+ ]);
3073
+ expect(onPostCommit).toHaveBeenCalledTimes(1);
3074
+ call = onPostCommit.mock.calls[0];
3075
+ expect(call[0]).toEqual('test');
3076
+ expect(call[3]).toEqual(Scheduler.unstable_now());
3077
+ expect(call[4]).toMatchInteractions(
3078
+ ReactFeatureFlags.enableSchedulerTracing
3079
+ ? [interactionLowPri]
3080
+ : [],
3081
+ );
3082
3063
- expect(onInteractionTraced).toHaveBeenCalledTimes(2);
3064
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
3083
+ expect(onInteractionTraced).toHaveBeenCalledTimes(2);
3084
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(
3085
+ 1,
3086
+ );
3087
3066
- // Work might be started multiple times before being completed.
3067
- // This is okay; it's part of the scheduler/tracing contract.
3068
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(3);
3069
- expect(
3070
- getWorkForReactThreads(onWorkStarted)[1][0],
3071
- ).toMatchInteractions([interactionLowPri, interactionHighPri]);
3072
- expect(
3073
- getWorkForReactThreads(onWorkStarted)[2][0],
3074
- ).toMatchInteractions([interactionLowPri]);
3075
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(2);
3076
- expect(
3077
- getWorkForReactThreads(onWorkStopped)[0][0],
3078
- ).toMatchInteractions([interactionLowPri, interactionHighPri]);
3079
- expect(
3080
- getWorkForReactThreads(onWorkStopped)[1][0],
3081
- ).toMatchInteractions([interactionLowPri]);
3082
- },
3083
- );
3088
+ // Work might be started multiple times before being completed.
3089
+ // This is okay; it's part of the scheduler/tracing contract.
3090
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(3);
3091
+ expect(
3092
+ getWorkForReactThreads(onWorkStarted)[1][0],
3093
+ ).toMatchInteractions([interactionLowPri, interactionHighPri]);
3094
+ expect(
3095
+ getWorkForReactThreads(onWorkStarted)[2][0],
3096
+ ).toMatchInteractions([interactionLowPri]);
3097
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(2);
3098
+ expect(
3099
+ getWorkForReactThreads(onWorkStopped)[0][0],
3100
+ ).toMatchInteractions([interactionLowPri, interactionHighPri]);
3101
+ expect(
3102
+ getWorkForReactThreads(onWorkStopped)[1][0],
3103
+ ).toMatchInteractions([interactionLowPri]);
3104
+ },
3105
+ );
3106
3085
- expect(onInteractionTraced).toHaveBeenCalledTimes(2);
3086
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
3087
- expect(
3088
- onInteractionScheduledWorkCompleted,
3089
- ).toHaveBeenLastNotifiedOfInteraction(interactionLowPri);
3090
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(3);
3091
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(2);
3107
+ expect(onInteractionTraced).toHaveBeenCalledTimes(2);
3108
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
3109
+ expect(
3110
+ onInteractionScheduledWorkCompleted,
3111
+ ).toHaveBeenLastNotifiedOfInteraction(interactionLowPri);
3112
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(3);
3113
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(2);
3114
+ });
3115
});
3116
3117
it('should trace work spawned by a commit phase lifecycle and setState callback', () => {
@@ -3110,7 +3133,7 @@ describe('Profiler', () => {
3133
render() {
3134
instance = this;
3135
Scheduler.unstable_yieldValue('Example:' + this.state.count);
3113
- return null;
3136
+ return <ComponentWithPassiveEffect count={this.state.count} />;
3137
}
3138
}
3139
@@ -3124,214 +3147,217 @@ describe('Profiler', () => {
3147
const onPostCommit = jest.fn(() => {
3148
Scheduler.unstable_yieldValue('onPostCommit');
3149
});
3127
- let firstCommitTime = Scheduler.unstable_now();
3128
- SchedulerTracing.unstable_trace(
3129
- interactionOne.name,
3130
- Scheduler.unstable_now(),
3131
- () => {
3132
- ReactTestRenderer.create(
3133
- <React.Profiler id="test" onPostCommit={onPostCommit}>
3134
- <Example />
3135
- </React.Profiler>,
3136
- {unstable_isConcurrent: true},
3137
- );
3138
- },
3139
- );
3150
3141
- expect(onInteractionTraced).toHaveBeenCalledTimes(1);
3142
- expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
3143
- interactionOne,
3144
- );
3145
- expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
3146
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
3147
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
3151
+ ReactTestRendererAct(() => {
3152
+ let firstCommitTime = Scheduler.unstable_now();
3153
+ SchedulerTracing.unstable_trace(
3154
+ interactionOne.name,
3155
+ Scheduler.unstable_now(),
3156
+ () => {
3157
+ ReactTestRenderer.create(
3158
+ <React.Profiler id="test" onPostCommit={onPostCommit}>
3159
+ <Example />
3160
+ </React.Profiler>,
3161
+ {unstable_isConcurrent: true},
3162
+ );
3163
+ },
3164
+ );
3165
3149
- expect(Scheduler).toFlushAndYield([
3150
- 'Example:0',
3151
- 'onPostCommit',
3152
- 'Example:1',
3153
- 'onPostCommit',
3154
- ]);
3166
+ expect(onInteractionTraced).toHaveBeenCalledTimes(1);
3167
+ expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
3168
+ interactionOne,
3169
+ );
3170
+ expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
3171
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
3172
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
3173
3156
- expect(onInteractionTraced).toHaveBeenCalledTimes(1);
3157
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
3158
- expect(
3159
- onInteractionScheduledWorkCompleted,
3160
- ).toHaveBeenLastNotifiedOfInteraction(interactionOne);
3161
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(2);
3162
- expect(getWorkForReactThreads(onWorkStarted)[0][0]).toMatchInteractions([
3163
- interactionOne,
3164
- ]);
3165
- expect(getWorkForReactThreads(onWorkStarted)[1][0]).toMatchInteractions([
3166
- interactionOne,
3167
- ]);
3168
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(2);
3169
- expect(getWorkForReactThreads(onWorkStopped)[0][0]).toMatchInteractions([
3170
- interactionOne,
3171
- ]);
3172
- expect(getWorkForReactThreads(onWorkStopped)[1][0]).toMatchInteractions([
3173
- interactionOne,
3174
- ]);
3175
-
3176
- expect(onPostCommit).toHaveBeenCalledTimes(2);
3177
- let call = onPostCommit.mock.calls[0];
3178
- expect(call[0]).toEqual('test');
3179
- expect(call[3]).toEqual(firstCommitTime);
3180
- expect(call[4]).toMatchInteractions(
3181
- ReactFeatureFlags.enableSchedulerTracing ? [interactionOne] : [],
3182
- );
3183
- call = onPostCommit.mock.calls[1];
3184
- expect(call[0]).toEqual('test');
3185
- expect(call[3]).toEqual(Scheduler.unstable_now());
3186
- expect(call[4]).toMatchInteractions(
3187
- ReactFeatureFlags.enableSchedulerTracing ? [interactionOne] : [],
3188
- );
3174
+ expect(Scheduler).toFlushAndYield([
3175
+ 'Example:0',
3176
+ 'onPostCommit',
3177
+ 'Example:1',
3178
+ 'onPostCommit',
3179
+ ]);
3180
3190
- onPostCommit.mockClear();
3181
+ expect(onInteractionTraced).toHaveBeenCalledTimes(1);
3182
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
3183
+ expect(
3184
+ onInteractionScheduledWorkCompleted,
3185
+ ).toHaveBeenLastNotifiedOfInteraction(interactionOne);
3186
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(2);
3187
+ expect(
3188
+ getWorkForReactThreads(onWorkStarted)[0][0],
3189
+ ).toMatchInteractions([interactionOne]);
3190
+ expect(
3191
+ getWorkForReactThreads(onWorkStarted)[1][0],
3192
+ ).toMatchInteractions([interactionOne]);
3193
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(2);
3194
+ expect(
3195
+ getWorkForReactThreads(onWorkStopped)[0][0],
3196
+ ).toMatchInteractions([interactionOne]);
3197
+ expect(
3198
+ getWorkForReactThreads(onWorkStopped)[1][0],
3199
+ ).toMatchInteractions([interactionOne]);
3200
3192
- const interactionTwo = {
3193
- id: 1,
3194
- name: 'componentDidUpdate test',
3195
- timestamp: Scheduler.unstable_now(),
3196
- };
3201
+ expect(onPostCommit).toHaveBeenCalledTimes(2);
3202
+ let call = onPostCommit.mock.calls[0];
3203
+ expect(call[0]).toEqual('test');
3204
+ expect(call[3]).toEqual(firstCommitTime);
3205
+ expect(call[4]).toMatchInteractions(
3206
+ ReactFeatureFlags.enableSchedulerTracing ? [interactionOne] : [],
3207
+ );
3208
+ call = onPostCommit.mock.calls[1];
3209
+ expect(call[0]).toEqual('test');
3210
+ expect(call[3]).toEqual(Scheduler.unstable_now());
3211
+ expect(call[4]).toMatchInteractions(
3212
+ ReactFeatureFlags.enableSchedulerTracing ? [interactionOne] : [],
3213
+ );
3214
3198
- // Cause an traced, async update
3199
- SchedulerTracing.unstable_trace(
3200
- interactionTwo.name,
3201
- Scheduler.unstable_now(),
3202
- () => {
3203
- instance.setState({count: 2});
3204
- },
3205
- );
3206
- expect(onPostCommit).not.toHaveBeenCalled();
3207
- expect(onInteractionTraced).toHaveBeenCalledTimes(2);
3208
- expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
3209
- interactionTwo,
3210
- );
3211
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
3212
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(2);
3213
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(2);
3215
+ onPostCommit.mockClear();
3216
3215
- Scheduler.unstable_advanceTime(5);
3217
+ const interactionTwo = {
3218
+ id: 1,
3219
+ name: 'componentDidUpdate test',
3220
+ timestamp: Scheduler.unstable_now(),
3221
+ };
3222
3217
- // Flush async work (outside of traced scope)
3218
- // This will cause an intentional cascading update from did-update
3219
- firstCommitTime = Scheduler.unstable_now();
3220
- expect(Scheduler).toFlushAndYield([
3221
- 'Example:2',
3222
- 'onPostCommit',
3223
- 'Example:3',
3224
- 'onPostCommit',
3225
- ]);
3226
-
3227
- expect(onInteractionTraced).toHaveBeenCalledTimes(2);
3228
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
3229
- expect(
3230
- onInteractionScheduledWorkCompleted,
3231
- ).toHaveBeenLastNotifiedOfInteraction(interactionTwo);
3232
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(4);
3233
- expect(getWorkForReactThreads(onWorkStarted)[2][0]).toMatchInteractions([
3234
- interactionTwo,
3235
- ]);
3236
- expect(getWorkForReactThreads(onWorkStarted)[3][0]).toMatchInteractions([
3237
- interactionTwo,
3238
- ]);
3239
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(4);
3240
- expect(getWorkForReactThreads(onWorkStopped)[2][0]).toMatchInteractions([
3241
- interactionTwo,
3242
- ]);
3243
- expect(getWorkForReactThreads(onWorkStopped)[3][0]).toMatchInteractions([
3244
- interactionTwo,
3245
- ]);
3246
-
3247
- // Verify the cascading commit is associated with the origin event
3248
- expect(onPostCommit).toHaveBeenCalledTimes(2);
3249
- call = onPostCommit.mock.calls[0];
3250
- expect(call[0]).toEqual('test');
3251
- expect(call[3]).toEqual(firstCommitTime);
3252
- expect(call[4]).toMatchInteractions(
3253
- ReactFeatureFlags.enableSchedulerTracing ? [interactionTwo] : [],
3254
- );
3255
- call = onPostCommit.mock.calls[1];
3256
- expect(call[0]).toEqual('test');
3257
- expect(call[3]).toEqual(Scheduler.unstable_now());
3258
- expect(call[4]).toMatchInteractions(
3259
- ReactFeatureFlags.enableSchedulerTracing ? [interactionTwo] : [],
3260
- );
3223
+ // Cause an traced, async update
3224
+ SchedulerTracing.unstable_trace(
3225
+ interactionTwo.name,
3226
+ Scheduler.unstable_now(),
3227
+ () => {
3228
+ instance.setState({count: 2});
3229
+ },
3230
+ );
3231
+ expect(onPostCommit).not.toHaveBeenCalled();
3232
+ expect(onInteractionTraced).toHaveBeenCalledTimes(2);
3233
+ expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
3234
+ interactionTwo,
3235
+ );
3236
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
3237
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(2);
3238
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(2);
3239
3262
- onPostCommit.mockClear();
3240
+ Scheduler.unstable_advanceTime(5);
3241
3264
- const interactionThree = {
3265
- id: 2,
3266
- name: 'setState callback test',
3267
- timestamp: Scheduler.unstable_now(),
3268
- };
3242
+ // Flush async work (outside of traced scope)
3243
+ // This will cause an intentional cascading update from did-update
3244
+ firstCommitTime = Scheduler.unstable_now();
3245
+ expect(Scheduler).toFlushAndYield([
3246
+ 'Example:2',
3247
+ 'onPostCommit',
3248
+ 'Example:3',
3249
+ 'onPostCommit',
3250
+ ]);
3251
3270
- // Cause a cascading update from the setState callback
3271
- function callback() {
3272
- instance.setState({count: 6});
3273
- }
3274
- SchedulerTracing.unstable_trace(
3275
- interactionThree.name,
3276
- Scheduler.unstable_now(),
3277
- () => {
3278
- instance.setState({count: 5}, callback);
3279
- },
3280
- );
3281
- expect(onPostCommit).not.toHaveBeenCalled();
3252
+ expect(onInteractionTraced).toHaveBeenCalledTimes(2);
3253
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
3254
+ expect(
3255
+ onInteractionScheduledWorkCompleted,
3256
+ ).toHaveBeenLastNotifiedOfInteraction(interactionTwo);
3257
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(4);
3258
+ expect(
3259
+ getWorkForReactThreads(onWorkStarted)[2][0],
3260
+ ).toMatchInteractions([interactionTwo]);
3261
+ expect(
3262
+ getWorkForReactThreads(onWorkStarted)[3][0],
3263
+ ).toMatchInteractions([interactionTwo]);
3264
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(4);
3265
+ expect(
3266
+ getWorkForReactThreads(onWorkStopped)[2][0],
3267
+ ).toMatchInteractions([interactionTwo]);
3268
+ expect(
3269
+ getWorkForReactThreads(onWorkStopped)[3][0],
3270
+ ).toMatchInteractions([interactionTwo]);
3271
3283
- expect(onInteractionTraced).toHaveBeenCalledTimes(3);
3284
- expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
3285
- interactionThree,
3286
- );
3287
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
3288
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(4);
3289
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(4);
3290
-
3291
- // Flush async work (outside of traced scope)
3292
- // This will cause an intentional cascading update from the setState callback
3293
- firstCommitTime = Scheduler.unstable_now();
3294
- expect(Scheduler).toFlushAndYield([
3295
- 'Example:5',
3296
- 'onPostCommit',
3297
- 'Example:6',
3298
- 'onPostCommit',
3299
- ]);
3300
-
3301
- expect(onInteractionTraced).toHaveBeenCalledTimes(3);
3302
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(3);
3303
- expect(
3304
- onInteractionScheduledWorkCompleted,
3305
- ).toHaveBeenLastNotifiedOfInteraction(interactionThree);
3306
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(6);
3307
- expect(getWorkForReactThreads(onWorkStarted)[4][0]).toMatchInteractions([
3308
- interactionThree,
3309
- ]);
3310
- expect(getWorkForReactThreads(onWorkStarted)[5][0]).toMatchInteractions([
3311
- interactionThree,
3312
- ]);
3313
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(6);
3314
- expect(getWorkForReactThreads(onWorkStopped)[4][0]).toMatchInteractions([
3315
- interactionThree,
3316
- ]);
3317
- expect(getWorkForReactThreads(onWorkStopped)[5][0]).toMatchInteractions([
3318
- interactionThree,
3319
- ]);
3320
-
3321
- // Verify the cascading commit is associated with the origin event
3322
- expect(onPostCommit).toHaveBeenCalledTimes(2);
3323
- call = onPostCommit.mock.calls[0];
3324
- expect(call[0]).toEqual('test');
3325
- expect(call[3]).toEqual(firstCommitTime);
3326
- expect(call[4]).toMatchInteractions(
3327
- ReactFeatureFlags.enableSchedulerTracing ? [interactionThree] : [],
3328
- );
3329
- call = onPostCommit.mock.calls[1];
3330
- expect(call[0]).toEqual('test');
3331
- expect(call[3]).toEqual(Scheduler.unstable_now());
3332
- expect(call[4]).toMatchInteractions(
3333
- ReactFeatureFlags.enableSchedulerTracing ? [interactionThree] : [],
3334
- );
3272
+ // Verify the cascading commit is associated with the origin event
3273
+ expect(onPostCommit).toHaveBeenCalledTimes(2);
3274
+ call = onPostCommit.mock.calls[0];
3275
+ expect(call[0]).toEqual('test');
3276
+ expect(call[3]).toEqual(firstCommitTime);
3277
+ expect(call[4]).toMatchInteractions(
3278
+ ReactFeatureFlags.enableSchedulerTracing ? [interactionTwo] : [],
3279
+ );
3280
+ call = onPostCommit.mock.calls[1];
3281
+ expect(call[0]).toEqual('test');
3282
+ expect(call[3]).toEqual(Scheduler.unstable_now());
3283
+ expect(call[4]).toMatchInteractions(
3284
+ ReactFeatureFlags.enableSchedulerTracing ? [interactionTwo] : [],
3285
+ );
3286
+
3287
+ onPostCommit.mockClear();
3288
+
3289
+ const interactionThree = {
3290
+ id: 2,
3291
+ name: 'setState callback test',
3292
+ timestamp: Scheduler.unstable_now(),
3293
+ };
3294
+
3295
+ // Cause a cascading update from the setState callback
3296
+ function callback() {
3297
+ instance.setState({count: 6});
3298
+ }
3299
+ SchedulerTracing.unstable_trace(
3300
+ interactionThree.name,
3301
+ Scheduler.unstable_now(),
3302
+ () => {
3303
+ instance.setState({count: 5}, callback);
3304
+ },
3305
+ );
3306
+ expect(onPostCommit).not.toHaveBeenCalled();
3307
+
3308
+ expect(onInteractionTraced).toHaveBeenCalledTimes(3);
3309
+ expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
3310
+ interactionThree,
3311
+ );
3312
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(2);
3313
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(4);
3314
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(4);
3315
+
3316
+ // Flush async work (outside of traced scope)
3317
+ // This will cause an intentional cascading update from the setState callback
3318
+ firstCommitTime = Scheduler.unstable_now();
3319
+ expect(Scheduler).toFlushAndYield([
3320
+ 'Example:5',
3321
+ 'onPostCommit',
3322
+ 'Example:6',
3323
+ 'onPostCommit',
3324
+ ]);
3325
+
3326
+ expect(onInteractionTraced).toHaveBeenCalledTimes(3);
3327
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(3);
3328
+ expect(
3329
+ onInteractionScheduledWorkCompleted,
3330
+ ).toHaveBeenLastNotifiedOfInteraction(interactionThree);
3331
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(6);
3332
+ expect(
3333
+ getWorkForReactThreads(onWorkStarted)[4][0],
3334
+ ).toMatchInteractions([interactionThree]);
3335
+ expect(
3336
+ getWorkForReactThreads(onWorkStarted)[5][0],
3337
+ ).toMatchInteractions([interactionThree]);
3338
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(6);
3339
+ expect(
3340
+ getWorkForReactThreads(onWorkStopped)[4][0],
3341
+ ).toMatchInteractions([interactionThree]);
3342
+ expect(
3343
+ getWorkForReactThreads(onWorkStopped)[5][0],
3344
+ ).toMatchInteractions([interactionThree]);
3345
+
3346
+ // Verify the cascading commit is associated with the origin event
3347
+ expect(onPostCommit).toHaveBeenCalledTimes(2);
3348
+ call = onPostCommit.mock.calls[0];
3349
+ expect(call[0]).toEqual('test');
3350
+ expect(call[3]).toEqual(firstCommitTime);
3351
+ expect(call[4]).toMatchInteractions(
3352
+ ReactFeatureFlags.enableSchedulerTracing ? [interactionThree] : [],
3353
+ );
3354
+ call = onPostCommit.mock.calls[1];
3355
+ expect(call[0]).toEqual('test');
3356
+ expect(call[3]).toEqual(Scheduler.unstable_now());
3357
+ expect(call[4]).toMatchInteractions(
3358
+ ReactFeatureFlags.enableSchedulerTracing ? [interactionThree] : [],
3359
+ );
3360
+ });
3361
});
3362
3363
it('should trace interactions associated with a parent component state update', () => {
@@ -3343,7 +3369,7 @@ describe('Profiler', () => {
3369
class Child extends React.Component {
3370
render() {
3371
Scheduler.unstable_yieldValue('Child:' + this.props.count);
3346
- return null;
3372
+ return <ComponentWithPassiveEffect count={this.props.count} />;
3373
}
3374
}
3375
@@ -3363,56 +3389,58 @@ describe('Profiler', () => {
3389
3390
Scheduler.unstable_advanceTime(1);
3391
3366
- ReactTestRenderer.create(<Parent />, {
3367
- unstable_isConcurrent: true,
3368
- });
3369
- expect(Scheduler).toFlushAndYield(['Child:0', 'onPostCommit']);
3370
- onPostCommit.mockClear();
3392
+ ReactTestRendererAct(() => {
3393
+ ReactTestRenderer.create(<Parent />, {
3394
+ unstable_isConcurrent: true,
3395
+ });
3396
+ expect(Scheduler).toFlushAndYield(['Child:0', 'onPostCommit']);
3397
+ onPostCommit.mockClear();
3398
3372
- const interaction = {
3373
- id: 0,
3374
- name: 'parent interaction',
3375
- timestamp: Scheduler.unstable_now(),
3376
- };
3399
+ const interaction = {
3400
+ id: 0,
3401
+ name: 'parent interaction',
3402
+ timestamp: Scheduler.unstable_now(),
3403
+ };
3404
3378
- SchedulerTracing.unstable_trace(
3379
- interaction.name,
3380
- Scheduler.unstable_now(),
3381
- () => {
3382
- parentInstance.setState({count: 1});
3383
- },
3384
- );
3405
+ SchedulerTracing.unstable_trace(
3406
+ interaction.name,
3407
+ Scheduler.unstable_now(),
3408
+ () => {
3409
+ parentInstance.setState({count: 1});
3410
+ },
3411
+ );
3412
3386
- expect(onInteractionTraced).toHaveBeenCalledTimes(1);
3387
- expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
3388
- interaction,
3389
- );
3390
- expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
3391
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
3392
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
3413
+ expect(onInteractionTraced).toHaveBeenCalledTimes(1);
3414
+ expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
3415
+ interaction,
3416
+ );
3417
+ expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
3418
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
3419
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
3420
3394
- expect(onPostCommit).not.toHaveBeenCalled();
3395
- expect(Scheduler).toFlushAndYield(['Child:1', 'onPostCommit']);
3396
- expect(onPostCommit).toHaveBeenCalledTimes(1);
3397
- const call = onPostCommit.mock.calls[0];
3398
- expect(call[0]).toEqual('test-profiler');
3399
- expect(call[4]).toMatchInteractions(
3400
- ReactFeatureFlags.enableSchedulerTracing ? [interaction] : [],
3401
- );
3421
+ expect(onPostCommit).not.toHaveBeenCalled();
3422
+ expect(Scheduler).toFlushAndYield(['Child:1', 'onPostCommit']);
3423
+ expect(onPostCommit).toHaveBeenCalledTimes(1);
3424
+ const call = onPostCommit.mock.calls[0];
3425
+ expect(call[0]).toEqual('test-profiler');
3426
+ expect(call[4]).toMatchInteractions(
3427
+ ReactFeatureFlags.enableSchedulerTracing ? [interaction] : [],
3428
+ );
3429
3403
- expect(onInteractionTraced).toHaveBeenCalledTimes(1);
3404
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
3405
- expect(
3406
- onInteractionScheduledWorkCompleted,
3407
- ).toHaveBeenLastNotifiedOfInteraction(interaction);
3408
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
3409
- expect(getWorkForReactThreads(onWorkStarted)[0][0]).toMatchInteractions([
3410
- interaction,
3411
- ]);
3412
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(1);
3413
- expect(getWorkForReactThreads(onWorkStopped)[0][0]).toMatchInteractions([
3414
- interaction,
3415
- ]);
3430
+ expect(onInteractionTraced).toHaveBeenCalledTimes(1);
3431
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
3432
+ expect(
3433
+ onInteractionScheduledWorkCompleted,
3434
+ ).toHaveBeenLastNotifiedOfInteraction(interaction);
3435
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(1);
3436
+ expect(
3437
+ getWorkForReactThreads(onWorkStarted)[0][0],
3438
+ ).toMatchInteractions([interaction]);
3439
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(1);
3440
+ expect(
3441
+ getWorkForReactThreads(onWorkStopped)[0][0],
3442
+ ).toMatchInteractions([interaction]);
3443
+ });
3444
});
3445
3446
describe('suspense', () => {
@@ -3443,76 +3471,79 @@ describe('Profiler', () => {
3471
const onPostCommit = jest.fn(() => {
3472
Scheduler.unstable_yieldValue('onPostCommit');
3473
});
3446
- SchedulerTracing.unstable_trace(
3447
- interaction.name,
3448
- Scheduler.unstable_now(),
3449
- () => {
3450
- ReactNoop.render(
3451
- <React.Profiler id="test-profiler" onPostCommit={onPostCommit}>
3452
- <React.Suspense fallback={<Text text="Loading..." />}>
3453
- <AsyncText text="Async" ms={20000} />
3454
- </React.Suspense>
3455
- <Text text="Sync" />
3456
- <Monkey ref={monkey} />
3457
- </React.Profiler>,
3458
- );
3459
- },
3460
- );
3474
+ ReactNoop.act(async () => {
3475
+ SchedulerTracing.unstable_trace(
3476
+ interaction.name,
3477
+ Scheduler.unstable_now(),
3478
+ () => {
3479
+ ReactNoop.render(
3480
+ <React.Profiler id="test-profiler" onPostCommit={onPostCommit}>
3481
+ <React.Suspense fallback={<Text text="Loading..." />}>
3482
+ <AsyncText text="Async" ms={20000} />
3483
+ </React.Suspense>
3484
+ <Text text="Sync" />
3485
+ <Monkey ref={monkey} />
3486
+ <ComponentWithPassiveEffect />
3487
+ </React.Profiler>,
3488
+ );
3489
+ },
3490
+ );
3491
3462
- expect(onInteractionTraced).toHaveBeenCalledTimes(1);
3463
- expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
3464
- interaction,
3465
- );
3466
- expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
3467
- expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
3468
- expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
3492
+ expect(onInteractionTraced).toHaveBeenCalledTimes(1);
3493
+ expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
3494
+ interaction,
3495
+ );
3496
+ expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
3497
+ expect(getWorkForReactThreads(onWorkStarted)).toHaveLength(0);
3498
+ expect(getWorkForReactThreads(onWorkStopped)).toHaveLength(0);
3499
3470
- expect(Scheduler).toFlushAndYield([
3471
- 'Suspend [Async]',
3472
- 'Text [Loading...]',
3473
- 'Text [Sync]',
3474
- 'Monkey',
3475
- 'onPostCommit',
3476
- ]);
3477
- // Should have committed the placeholder.
3478
- expect(ReactNoop.getChildrenAsJSX()).toEqual('Loading...Sync');
3479
- expect(onPostCommit).toHaveBeenCalledTimes(1);
3500
+ expect(Scheduler).toFlushAndYield([
3501
+ 'Suspend [Async]',
3502
+ 'Text [Loading...]',
3503
+ 'Text [Sync]',
3504
+ 'Monkey',
3505
+ 'onPostCommit',
3506
+ ]);
3507
+ // Should have committed the placeholder.
3508
+ expect(ReactNoop.getChildrenAsJSX()).toEqual('Loading...Sync');
3509
+ expect(onPostCommit).toHaveBeenCalledTimes(1);
3510
3481
- let call = onPostCommit.mock.calls[0];
3482
- expect(call[0]).toEqual('test-profiler');
3483
- expect(call[4]).toMatchInteractions(
3484
- ReactFeatureFlags.enableSchedulerTracing ? [interaction] : [],
3485
- );
3511
+ let call = onPostCommit.mock.calls[0];
3512
+ expect(call[0]).toEqual('test-profiler');
3513
+ expect(call[4]).toMatchInteractions(
3514
+ ReactFeatureFlags.enableSchedulerTracing ? [interaction] : [],
3515
+ );
3516
3487
- expect(onInteractionTraced).toHaveBeenCalledTimes(1);
3488
- expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
3517
+ expect(onInteractionTraced).toHaveBeenCalledTimes(1);
3518
+ expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
3519
3490
- // An unrelated update in the middle shouldn't affect things...
3491
- monkey.current.forceUpdate();
3492
- expect(Scheduler).toFlushAndYield(['Monkey', 'onPostCommit']);
3493
- expect(onPostCommit).toHaveBeenCalledTimes(2);
3520
+ // An unrelated update in the middle shouldn't affect things...
3521
+ monkey.current.forceUpdate();
3522
+ expect(Scheduler).toFlushAndYield(['Monkey', 'onPostCommit']);
3523
+ expect(onPostCommit).toHaveBeenCalledTimes(2);
3524
3495
- // Once the promise resolves, we render the suspended view
3496
- await awaitableAdvanceTimers(20000);
3497
- expect(Scheduler).toHaveYielded(['Promise resolved [Async]']);
3498
- expect(Scheduler).toFlushAndYield([
3499
- 'AsyncText [Async]',
3500
- 'onPostCommit',
3501
- ]);
3502
- expect(ReactNoop.getChildrenAsJSX()).toEqual('AsyncSync');
3503
- expect(onPostCommit).toHaveBeenCalledTimes(3);
3525
+ // Once the promise resolves, we render the suspended view
3526
+ await awaitableAdvanceTimers(20000);
3527
+ expect(Scheduler).toHaveYielded(['Promise resolved [Async]']);
3528
+ expect(Scheduler).toFlushAndYield([
3529
+ 'AsyncText [Async]',
3530
+ 'onPostCommit',
3531
+ ]);
3532
+ expect(ReactNoop.getChildrenAsJSX()).toEqual('AsyncSync');
3533
+ expect(onPostCommit).toHaveBeenCalledTimes(3);
3534
3505
- call = onPostCommit.mock.calls[2];
3506
- expect(call[0]).toEqual('test-profiler');
3507
- expect(call[4]).toMatchInteractions(
3508
- ReactFeatureFlags.enableSchedulerTracing ? [interaction] : [],
3509
- );
3535
+ call = onPostCommit.mock.calls[2];
3536
+ expect(call[0]).toEqual('test-profiler');
3537
+ expect(call[4]).toMatchInteractions(
3538
+ ReactFeatureFlags.enableSchedulerTracing ? [interaction] : [],
3539
+ );
3540
3511
- expect(onInteractionTraced).toHaveBeenCalledTimes(1);
3512
- expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
3513
- expect(
3514
- onInteractionScheduledWorkCompleted,
3515
- ).toHaveBeenLastNotifiedOfInteraction(interaction);
3541
+ expect(onInteractionTraced).toHaveBeenCalledTimes(1);
3542
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
3543
+ expect(
3544
+ onInteractionScheduledWorkCompleted,
3545
+ ).toHaveBeenLastNotifiedOfInteraction(interaction);
3546
+ });
3547
});
3548
3549
it('does not prematurely complete for suspended sync renders', async () => {
@@ -3533,6 +3564,7 @@ describe('Profiler', () => {
3564
<React.Profiler id="app" onPostCommit={onPostCommit}>
3565
<React.Suspense fallback={<Text text="loading" />}>
3566
<AsyncText text="loaded" ms={500} />
3567
+ <ComponentWithPassiveEffect />
3568
</React.Suspense>
3569
</React.Profiler>,
3570
);
@@ -3594,6 +3626,7 @@ describe('Profiler', () => {
3626
() => {
3627
ReactTestRenderer.create(
3628
<React.Profiler id="app" onPostCommit={onPostCommit}>
3629
+ <ComponentWithPassiveEffect />
3630
<React.Suspense fallback={<Text text="loading" />}>
3631
<AsyncComponentWithCascadingWork text="loaded" ms={500} />
3632
</React.Suspense>
@@ -3616,7 +3649,16 @@ describe('Profiler', () => {
3649
3650
wrappedCascadingFn();
3651
expect(Scheduler).toHaveYielded(['onPostCommit', 'render']);
3619
- expect(Scheduler).toFlushAndYield(['onPostCommit']);
3652
+
3653
+ // The new reconciler does not call onPostCommit again
3654
+ // because the resolved suspended subtree doesn't contain any passive effects.
3655
+ // If <AsyncComponentWithCascadingWork> or its decendents had a passive effect,
3656
+ // onPostCommit would be called again.
3657
+ if (gate(flags => flags.new)) {
3658
+ expect(Scheduler).toFlushAndYield([]);
3659
+ } else {
3660
+ expect(Scheduler).toFlushAndYield(['onPostCommit']);
3661
+ }
3662
3663
expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
3664
expect(
@@ -3631,15 +3673,15 @@ describe('Profiler', () => {
3673
timestamp: Scheduler.unstable_now(),
3674
};
3675
3634
- const onPostCommit = jest.fn(() => {
3635
- Scheduler.unstable_yieldValue('onPostCommit');
3676
+ const onCommit = jest.fn(() => {
3677
+ Scheduler.unstable_yieldValue('onCommit');
3678
});
3679
SchedulerTracing.unstable_trace(
3680
interaction.name,
3681
interaction.timestamp,
3682
() => {
3683
ReactTestRenderer.create(
3642
- <React.Profiler id="app" onPostCommit={onPostCommit}>
3684
+ <React.Profiler id="app" onCommit={onCommit}>
3685
<React.Suspense fallback={<Text text="loading" />}>
3686
<AsyncText text="loaded" ms={500} />
3687
</React.Suspense>
@@ -3657,7 +3699,7 @@ describe('Profiler', () => {
3699
expect(Scheduler).toFlushAndYield([
3700
'Suspend [loaded]',
3701
'Text [loading]',
3660
- 'onPostCommit',
3702
+ 'onCommit',
3703
]);
3704
expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
3705
@@ -3665,10 +3707,7 @@ describe('Profiler', () => {
3707
await awaitableAdvanceTimers(500);
3708
3709
expect(Scheduler).toHaveYielded(['Promise resolved [loaded]']);
3668
- expect(Scheduler).toFlushAndYield([
3669
- 'AsyncText [loaded]',
3670
- 'onPostCommit',
3671
- ]);
3710
+ expect(Scheduler).toFlushAndYield(['AsyncText [loaded]', 'onCommit']);
3711
expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
3712
expect(
3713
onInteractionScheduledWorkCompleted,
@@ -3682,15 +3721,15 @@ describe('Profiler', () => {
3721
timestamp: Scheduler.unstable_now(),
3722
};
3723
3685
- const onPostCommit = jest.fn(() => {
3686
- Scheduler.unstable_yieldValue('onPostCommit');
3724
+ const onCommit = jest.fn(() => {
3725
+ Scheduler.unstable_yieldValue('onCommit');
3726
});
3727
SchedulerTracing.unstable_trace(
3728
interaction.name,
3729
interaction.timestamp,
3730
() => {
3731
ReactTestRenderer.create(
3693
- <React.Profiler id="app" onPostCommit={onPostCommit}>
3732
+ <React.Profiler id="app" onCommit={onCommit}>
3733
<React.Suspense fallback={<Text text="loading" />}>
3734
<AsyncText text="loaded" ms={100} />
3735
</React.Suspense>
@@ -3702,7 +3741,7 @@ describe('Profiler', () => {
3741
expect(Scheduler).toFlushAndYield([
3742
'Suspend [loaded]',
3743
'Text [loading]',
3705
- 'onPostCommit',
3744
+ 'onCommit',
3745
]);
3746
3747
expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
@@ -3710,10 +3749,7 @@ describe('Profiler', () => {
3749
jest.advanceTimersByTime(100);
3750
await resourcePromise;
3751
expect(Scheduler).toHaveYielded(['Promise resolved [loaded]']);
3713
- expect(Scheduler).toFlushAndYield([
3714
- 'AsyncText [loaded]',
3715
- 'onPostCommit',
3716
- ]);
3752
+ expect(Scheduler).toFlushAndYield(['AsyncText [loaded]', 'onCommit']);
3753
3754
expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
3755
expect(
@@ -3738,6 +3774,7 @@ describe('Profiler', () => {
3774
() => {
3775
renderer = ReactTestRenderer.create(
3776
<React.Profiler id="app" onPostCommit={onPostCommit}>
3777
+ <ComponentWithPassiveEffect />
3778
<React.Suspense fallback={<Text text="loading" />}>
3779
<AsyncText text="loaded" ms={100} />
3780
</React.Suspense>
@@ -3776,6 +3813,7 @@ describe('Profiler', () => {
3813
() => {
3814
renderer.update(
3815
<React.Profiler id="app" onPostCommit={onPostCommit}>
3816
+ <ComponentWithPassiveEffect />
3817
<React.Suspense fallback={<Text text="loading" />}>
3818
<AsyncText text="loaded" ms={100} />
3819
</React.Suspense>
@@ -3842,15 +3880,15 @@ describe('Profiler', () => {
3880
timestamp: Scheduler.unstable_now(),
3881
};
3882
3845
- const onPostCommit = jest.fn(() => {
3846
- Scheduler.unstable_yieldValue('onPostCommit');
3883
+ const onCommit = jest.fn(() => {
3884
+ Scheduler.unstable_yieldValue('onCommit');
3885
});
3886
SchedulerTracing.unstable_trace(
3887
initialRenderInteraction.name,
3888
initialRenderInteraction.timestamp,
3889
() => {
3890
renderer.update(
3853
- <React.Profiler id="app" onPostCommit={onPostCommit}>
3891
+ <React.Profiler id="app" onCommit={onCommit}>
3892
<React.Suspense fallback={<Text text="loading" />}>
3893
<AsyncText text="loaded" ms={100} />
3894
</React.Suspense>
@@ -3866,7 +3904,7 @@ describe('Profiler', () => {
3904
]);
3905
3906
expect(onInteractionScheduledWorkCompleted).not.toHaveBeenCalled();
3869
- expect(onPostCommit).not.toHaveBeenCalled();
3907
+ expect(onCommit).not.toHaveBeenCalled();
3908
3909
Scheduler.unstable_advanceTime(50);
3910
jest.advanceTimersByTime(50);
@@ -3885,7 +3923,7 @@ describe('Profiler', () => {
3923
highPriUpdateInteraction.timestamp,
3924
() => {
3925
renderer.update(
3888
- <React.Profiler id="app" onPostCommit={onPostCommit}>
3926
+ <React.Profiler id="app" onCommit={onCommit}>
3927
<React.Suspense fallback={<Text text="loading" />}>
3928
<AsyncText text="loaded" ms={100} />
3929
</React.Suspense>
@@ -3899,15 +3937,15 @@ describe('Profiler', () => {
3937
'Suspend [loaded]',
3938
'Text [loading]',
3939
'Text [updated]',
3940
+ 'onCommit',
3941
]);
3903
- expect(Scheduler).toFlushAndYieldThrough(['onPostCommit']);
3942
expect(renderer.toJSON()).toEqual(['loading', 'updated']);
3943
3906
- expect(onPostCommit).toHaveBeenCalledTimes(1);
3907
- expect(onPostCommit.mock.calls[0][4]).toMatchInteractions([
3944
+ expect(onCommit).toHaveBeenCalledTimes(1);
3945
+ expect(onCommit.mock.calls[0][4]).toMatchInteractions([
3946
highPriUpdateInteraction,
3947
]);
3910
- onPostCommit.mockClear();
3948
+ onCommit.mockClear();
3949
3950
expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(0);
3951
@@ -3915,14 +3953,11 @@ describe('Profiler', () => {
3953
jest.advanceTimersByTime(50);
3954
await originalPromise;
3955
expect(Scheduler).toHaveYielded(['Promise resolved [loaded]']);
3918
- expect(Scheduler).toFlushAndYield([
3919
- 'AsyncText [loaded]',
3920
- 'onPostCommit',
3921
- ]);
3956
+ expect(Scheduler).toFlushAndYield(['AsyncText [loaded]', 'onCommit']);
3957
expect(renderer.toJSON()).toEqual(['loaded', 'updated']);
3958
3924
- expect(onPostCommit).toHaveBeenCalledTimes(1);
3925
- expect(onPostCommit.mock.calls[0][4]).toMatchInteractions([
3959
+ expect(onCommit).toHaveBeenCalledTimes(1);
3960
+ expect(onCommit.mock.calls[0][4]).toMatchInteractions([
3961
highPriUpdateInteraction,
3962
]);
3963
@@ -3961,15 +3996,15 @@ describe('Profiler', () => {
3996
}
3997
};
3998
3964
- const onPostCommit = jest.fn(() => {
3965
- Scheduler.unstable_yieldValue('onPostCommit');
3999
+ const onCommit = jest.fn(() => {
4000
+ Scheduler.unstable_yieldValue('onCommit');
4001
});
4002
SchedulerTracing.unstable_trace(
4003
interaction.name,
4004
Scheduler.unstable_now(),
4005
() => {
4006
ReactNoop.render(
3972
- <React.Profiler id="test-profiler" onPostCommit={onPostCommit}>
4007
+ <React.Profiler id="test-profiler" onCommit={onCommit}>
4008
<React.Suspense fallback={<Text text="Loading..." />}>
4009
<AsyncText text="Async" ms={20000} />
4010
</React.Suspense>
@@ -3991,13 +4026,13 @@ describe('Profiler', () => {
4026
'Suspend [Async]',
4027
'Text [Loading...]',
4028
'Text [Sync]',
3994
- 'onPostCommit',
4029
+ 'onCommit',
4030
]);
4031
// Should have committed the placeholder.
4032
expect(ReactNoop.getChildrenAsJSX()).toEqual('Loading...Sync');
3998
- expect(onPostCommit).toHaveBeenCalledTimes(1);
4033
+ expect(onCommit).toHaveBeenCalledTimes(1);
4034
4000
- let call = onPostCommit.mock.calls[0];
4035
+ let call = onCommit.mock.calls[0];
4036
expect(call[0]).toEqual('test-profiler');
4037
expect(call[4]).toMatchInteractions(
4038
ReactFeatureFlags.enableSchedulerTracing ? [interaction] : [],
@@ -4013,15 +4048,12 @@ describe('Profiler', () => {
4048
// Once the promise resolves, we render the suspended view
4049
await awaitableAdvanceTimers(20000);
4050
expect(Scheduler).toHaveYielded(['Promise resolved [Async]']);
4016
- expect(Scheduler).toFlushAndYield([
4017
- 'AsyncText [Async]',
4018
- 'onPostCommit',
4019
- ]);
4051
+ expect(Scheduler).toFlushAndYield(['AsyncText [Async]', 'onCommit']);
4052
expect(ReactNoop.getChildrenAsJSX()).toEqual('AsyncSync');
4021
- expect(onPostCommit).toHaveBeenCalledTimes(2);
4053
+ expect(onCommit).toHaveBeenCalledTimes(2);
4054
4055
// No interactions should be associated with this update.
4024
- call = onPostCommit.mock.calls[1];
4056
+ call = onCommit.mock.calls[1];
4057
expect(call[0]).toEqual('test-profiler');
4058
expect(call[4]).toMatchInteractions([]);
4059
});