Skip to content

Commit 7a3d4ad

Browse files
committed
[DevTools] Handle reorders when resuspending while fallback contains Suspense
1 parent 90a3125 commit 7a3d4ad

File tree

2 files changed

+285
-75
lines changed

2 files changed

+285
-75
lines changed

packages/react-devtools-shared/src/__tests__/store-test.js

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2775,4 +2775,193 @@ describe('Store', () => {
27752775
<Suspense name="content" rects={[{x:1,y:2,width:4,height:1}]}>
27762776
`);
27772777
});
2778+
2779+
// @reactVersion >= 18.0
2780+
it('can reconcile resuspended Suspense with Suspense in fallback positions', async () => {
2781+
let resolveHeadFallback;
2782+
let resolveHeadContent;
2783+
let resolveMainFallback;
2784+
let resolveMainContent;
2785+
2786+
function Component({children, promise}) {
2787+
if (promise) {
2788+
React.use(promise);
2789+
}
2790+
return <div>{children}</div>;
2791+
}
2792+
2793+
function WithSuspenseInFallback({fallbackPromise, contentPromise, name}) {
2794+
return (
2795+
<React.Suspense
2796+
name={name}
2797+
fallback={
2798+
<React.Suspense
2799+
name={`${name}-fallback`}
2800+
fallback={
2801+
<Component key={`${name}-fallback-fallback`}>
2802+
Loading fallback...
2803+
</Component>
2804+
}>
2805+
<Component
2806+
key={`${name}-fallback-content`}
2807+
promise={fallbackPromise}>
2808+
Loading...
2809+
</Component>
2810+
</React.Suspense>
2811+
}>
2812+
<Component key={`${name}-content`} promise={contentPromise}>
2813+
done
2814+
</Component>
2815+
</React.Suspense>
2816+
);
2817+
}
2818+
2819+
function App({
2820+
headFallbackPromise,
2821+
headContentPromise,
2822+
mainContentPromise,
2823+
mainFallbackPromise,
2824+
tailContentPromise,
2825+
tailFallbackPromise,
2826+
}) {
2827+
return (
2828+
<>
2829+
<WithSuspenseInFallback
2830+
fallbackPromise={headFallbackPromise}
2831+
contentPromise={headContentPromise}
2832+
name="head"
2833+
/>
2834+
<WithSuspenseInFallback
2835+
fallbackPromise={mainFallbackPromise}
2836+
contentPromise={mainContentPromise}
2837+
name="main"
2838+
/>
2839+
</>
2840+
);
2841+
}
2842+
2843+
const initialHeadContentPromise = new Promise(resolve => {
2844+
resolveHeadContent = resolve;
2845+
});
2846+
const initialHeadFallbackPromise = new Promise(resolve => {
2847+
resolveHeadFallback = resolve;
2848+
});
2849+
const initialMainContentPromise = new Promise(resolve => {
2850+
resolveMainContent = resolve;
2851+
});
2852+
const initialMainFallbackPromise = new Promise(resolve => {
2853+
resolveMainFallback = resolve;
2854+
});
2855+
await actAsync(() =>
2856+
render(
2857+
<App
2858+
headFallbackPromise={initialHeadFallbackPromise}
2859+
headContentPromise={initialHeadContentPromise}
2860+
mainContentPromise={initialMainContentPromise}
2861+
mainFallbackPromise={initialMainFallbackPromise}
2862+
/>,
2863+
),
2864+
);
2865+
2866+
expect(store).toMatchInlineSnapshot(`
2867+
[root]
2868+
▾ <App>
2869+
▾ <WithSuspenseInFallback>
2870+
▾ <Suspense name="head">
2871+
▾ <Suspense name="head-fallback">
2872+
<Component key="head-fallback-fallback">
2873+
▾ <WithSuspenseInFallback>
2874+
▾ <Suspense name="main">
2875+
▾ <Suspense name="main-fallback">
2876+
<Component key="main-fallback-fallback">
2877+
[shell]
2878+
<Suspense name="head" rects={null}>
2879+
<Suspense name="head-fallback" rects={null}>
2880+
<Suspense name="main" rects={null}>
2881+
<Suspense name="main-fallback" rects={null}>
2882+
`);
2883+
2884+
await actAsync(() => {
2885+
resolveHeadFallback();
2886+
resolveMainFallback();
2887+
resolveHeadContent();
2888+
resolveMainContent();
2889+
});
2890+
2891+
expect(store).toMatchInlineSnapshot(`
2892+
[root]
2893+
▾ <App>
2894+
▾ <WithSuspenseInFallback>
2895+
▾ <Suspense name="head">
2896+
<Component key="head-content">
2897+
▾ <WithSuspenseInFallback>
2898+
▾ <Suspense name="main">
2899+
<Component key="main-content">
2900+
[shell]
2901+
<Suspense name="head" rects={[{x:1,y:2,width:4,height:1}]}>
2902+
<Suspense name="main" rects={[{x:1,y:2,width:4,height:1}]}>
2903+
`);
2904+
2905+
// Resuspend head content
2906+
const nextHeadContentPromise = new Promise(resolve => {
2907+
resolveHeadContent = resolve;
2908+
});
2909+
await actAsync(() =>
2910+
render(
2911+
<App
2912+
headFallbackPromise={initialHeadFallbackPromise}
2913+
headContentPromise={nextHeadContentPromise}
2914+
mainContentPromise={initialMainContentPromise}
2915+
mainFallbackPromise={initialMainFallbackPromise}
2916+
/>,
2917+
),
2918+
);
2919+
2920+
expect(store).toMatchInlineSnapshot(`
2921+
[root]
2922+
▾ <App>
2923+
▾ <WithSuspenseInFallback>
2924+
▾ <Suspense name="head">
2925+
▾ <Suspense name="head-fallback">
2926+
<Component key="head-fallback-content">
2927+
▾ <WithSuspenseInFallback>
2928+
▾ <Suspense name="main">
2929+
<Component key="main-content">
2930+
[shell]
2931+
<Suspense name="head" rects={[{x:1,y:2,width:4,height:1}]}>
2932+
<Suspense name="head-fallback" rects={[{x:1,y:2,width:10,height:1}]}>
2933+
<Suspense name="main" rects={[{x:1,y:2,width:4,height:1}]}>
2934+
`);
2935+
2936+
// Resuspend head fallback
2937+
const nextHeadFallbackPromise = new Promise(resolve => {
2938+
resolveHeadFallback = resolve;
2939+
});
2940+
await actAsync(() =>
2941+
render(
2942+
<App
2943+
headFallbackPromise={nextHeadFallbackPromise}
2944+
headContentPromise={nextHeadContentPromise}
2945+
mainContentPromise={initialMainContentPromise}
2946+
mainFallbackPromise={initialMainFallbackPromise}
2947+
/>,
2948+
),
2949+
);
2950+
2951+
expect(store).toMatchInlineSnapshot(`
2952+
[root]
2953+
▾ <App>
2954+
▾ <WithSuspenseInFallback>
2955+
▾ <Suspense name="head">
2956+
▾ <Suspense name="head-fallback">
2957+
<Component key="head-fallback-fallback">
2958+
▾ <WithSuspenseInFallback>
2959+
▾ <Suspense name="main">
2960+
<Component key="main-content">
2961+
[shell]
2962+
<Suspense name="head" rects={[{x:1,y:2,width:4,height:1}]}>
2963+
<Suspense name="head-fallback" rects={[{x:1,y:2,width:10,height:1}]}>
2964+
<Suspense name="main" rects={[{x:1,y:2,width:4,height:1}]}>
2965+
`);
2966+
});
27782967
});

0 commit comments

Comments
 (0)