Mark spawned work for client-rendered suspense boundary (#16341)
Currently this is getting marked as Never which is the normal continuation for a dehydrated boundary, but if it is client-rendered it has a higher priority. That causes us to drop the interaction tracing for that render. This colocates the marking where we actually set the expiration time.
Sebastian Markbåge committed
Aug 9, 2019 at 12:37 UTC
07d062dea852befa47d5b11111c21769bd8205d3
3 files changed
+81
-4
packages/react-reconciler/src/ReactFiberBeginWork.js
+9
-1
@@ -1903,12 +1903,20 @@ function updateDehydratedSuspenseComponent(
1903
// they should be.
1904
let serverDisplayTime = requestCurrentTime();
1905
// Schedule a normal pri update to render this content.
1906
- workInProgress.expirationTime = computeAsyncExpiration(serverDisplayTime);
1906
+ let newExpirationTime = computeAsyncExpiration(serverDisplayTime);
1907
+ if (enableSchedulerTracing) {
1908
+ markSpawnedWork(newExpirationTime);
1909
+ }
1910
+ workInProgress.expirationTime = newExpirationTime;
1911
} else {
1912
// We'll continue hydrating the rest at offscreen priority since we'll already
1913
// be showing the right content coming from the server, it is no rush.
1914
workInProgress.expirationTime = Never;
1915
+ if (enableSchedulerTracing) {
1916
+ markSpawnedWork(Never);
1917
+ }
1918
}
1919
+
1920
return null;
1921
}
1922
packages/react-reconciler/src/ReactFiberCompleteWork.js
-3
@@ -979,9 +979,6 @@ function completeWork(
979
'A dehydrated suspense component was completed without a hydrated node. ' +
980
'This is probably a bug in React.',
981
);
982
- if (enableSchedulerTracing) {
983
- markSpawnedWork(Never);
984
- }
982
skipPastDehydratedSuspenseInstance(workInProgress);
983
} else {
984
// We should never have been in a hydration state if we didn't have a current.
packages/react/src/__tests__/ReactDOMTracing-test.internal.js
+72
@@ -710,6 +710,78 @@ describe('ReactDOMTracing', () => {
710
711
done();
712
});
713
+
714
+ it('traces interaction across client-rendered hydration', async done => {
715
+ let suspend = false;
716
+ let promise = new Promise(() => {});
717
+ let ref = React.createRef();
718
+
719
+ function Child() {
720
+ if (suspend) {
721
+ throw promise;
722
+ } else {
723
+ return 'Hello';
724
+ }
725
+ }
726
+
727
+ function App() {
728
+ return (
729
+ <div>
730
+ <React.Suspense fallback="Loading...">
731
+ <span ref={ref}>
732
+ <Child />
733
+ </span>
734
+ </React.Suspense>
735
+ </div>
736
+ );
737
+ }
738
+
739
+ // Render the final HTML.
740
+ suspend = true;
741
+ const finalHTML = ReactDOMServer.renderToString(<App />);
742
+
743
+ const container = document.createElement('div');
744
+ container.innerHTML = finalHTML;
745
+
746
+ let interaction;
747
+
748
+ const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
749
+
750
+ // Hydrate without suspending to fill in the client-rendered content.
751
+ suspend = false;
752
+ SchedulerTracing.unstable_trace('initialization', 0, () => {
753
+ interaction = Array.from(SchedulerTracing.unstable_getCurrent())[0];
754
+
755
+ root.render(<App />);
756
+ });
757
+
758
+ expect(onWorkStopped).toHaveBeenCalledTimes(1);
759
+
760
+ // Advance time a bit so that we get into a new expiration bucket.
761
+ Scheduler.unstable_advanceTime(300);
762
+ jest.advanceTimersByTime(300);
763
+
764
+ Scheduler.unstable_flushAll();
765
+ jest.runAllTimers();
766
+
767
+ expect(ref.current).not.toBe(null);
768
+
769
+ // We should've had two commits that was traced.
770
+ // First one that hydrates the parent, and then one that hydrates
771
+ // the boundary at higher than Never priority.
772
+ expect(onWorkStopped).toHaveBeenCalledTimes(3);
773
+
774
+ expect(onInteractionTraced).toHaveBeenCalledTimes(1);
775
+ expect(onInteractionTraced).toHaveBeenLastNotifiedOfInteraction(
776
+ interaction,
777
+ );
778
+ expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
779
+ expect(
780
+ onInteractionScheduledWorkCompleted,
781
+ ).toHaveBeenLastNotifiedOfInteraction(interaction);
782
+
783
+ done();
784
+ });
785
});
786
});
787
});