Skip to content

Commit 2bcabce

Browse files
committed
Use flushSync for useLoadableQuery helper
1 parent 3eae4b2 commit 2bcabce

1 file changed

Lines changed: 38 additions & 19 deletions

File tree

src/react/hooks/__tests__/useLoadableQuery/customScalars.test.tsx

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
} from "@testing-library/react-render-stream";
88
import { userEvent } from "@testing-library/user-event";
99
import React, { Suspense } from "react";
10+
import { flushSync } from "react-dom";
1011
import { ErrorBoundary } from "react-error-boundary";
1112
import { delay, of } from "rxjs";
1213

@@ -69,11 +70,16 @@ async function renderHook<
6970
return null;
7071
}
7172

73+
type RefetchFunction = useLoadableQuery.Handlers<
74+
TData,
75+
TVariables
76+
>["refetch"];
77+
7278
function App({ props }: { props: Props | undefined }) {
7379
useTrackRenders({ name: "useLoadableQuery" });
74-
const [loadQuery, queryRef] = renderHookImpl(props as any);
80+
const [loadQuery, queryRef, { refetch }] = renderHookImpl(props as any);
7581

76-
mergeSnapshot({ loadQuery });
82+
mergeSnapshot({ loadQuery, refetch });
7783

7884
return (
7985
<Suspense fallback={<SuspenseFallback />}>
@@ -96,10 +102,15 @@ async function renderHook<
96102
} = createRenderStream<
97103
| {
98104
loadQuery: useLoadableQuery.LoadQueryFunction<TVariables>;
105+
refetch: RefetchFunction;
99106
result?: useReadQuery.Result<TData, TStates>;
100107
}
101108
| { error: ErrorLike }
102-
>({ initialSnapshot: { loadQuery: null as any } });
109+
>({
110+
// These values should always be available, but createRenderStream needs an
111+
// initial snapshot when using mergeSnapshot so we provide it with something
112+
initialSnapshot: { loadQuery: null as any, refetch: null as any },
113+
});
103114

104115
const utils = await render(<App props={options.initialProps} />, options);
105116

@@ -117,7 +128,21 @@ async function renderHook<
117128
return snapshot;
118129
}
119130

120-
return { takeRender, rerender, getCurrentSnapshot };
131+
// React 18 skips committing the suspense fallback when loadQuery/refetch is
132+
// triggered as a default-priority update, so the fallback render is missed
133+
// and tests may fail. flushSync forces a synchronous commit so the fallback
134+
// renders in both React 18 and 19, while returning the underlying result.
135+
const loadQuery: useLoadableQuery.LoadQueryFunction<TVariables> = (
136+
...args
137+
) => {
138+
return flushSync(() => getCurrentSnapshot().loadQuery(...args));
139+
};
140+
141+
const refetch: RefetchFunction = (...args) => {
142+
return flushSync(() => getCurrentSnapshot().refetch(...args));
143+
};
144+
145+
return { takeRender, rerender, getCurrentSnapshot, refetch, loadQuery };
121146
}
122147

123148
test("serializes scalar variables used in field arguments", async () => {
@@ -452,15 +477,9 @@ test("preserves referential identity when refetching identical serialized scalar
452477
),
453478
});
454479

455-
let refetch!: useLoadableQuery.Result["2"]["refetch"];
456-
457480
using _disabledAct = disableActEnvironment();
458-
const { takeRender, getCurrentSnapshot } = await renderHook(
459-
() => {
460-
const result = useLoadableQuery(query);
461-
refetch = result[2].refetch;
462-
return result;
463-
},
481+
const { takeRender, refetch, loadQuery } = await renderHook(
482+
() => useLoadableQuery(query),
464483
{ wrapper: createClientWrapper(client) }
465484
);
466485

@@ -470,7 +489,7 @@ test("preserves referential identity when refetching identical serialized scalar
470489
expect(renderedComponents).toStrictEqual(["useLoadableQuery"]);
471490
}
472491

473-
getCurrentSnapshot().loadQuery();
492+
loadQuery();
474493

475494
{
476495
const { renderedComponents } = await takeRender();
@@ -590,7 +609,7 @@ test("serializes scalar fields in the error with a `none` error policy", async (
590609
});
591610

592611
using _disabledAct = disableActEnvironment();
593-
const { takeRender, getCurrentSnapshot } = await renderHook(
612+
const { takeRender, loadQuery } = await renderHook(
594613
() => useLoadableQuery(query, { errorPolicy: "none" }),
595614
{ wrapper: createClientWrapper(client) }
596615
);
@@ -601,7 +620,7 @@ test("serializes scalar fields in the error with a `none` error policy", async (
601620
expect(renderedComponents).toStrictEqual(["useLoadableQuery"]);
602621
}
603622

604-
getCurrentSnapshot().loadQuery();
623+
loadQuery();
605624

606625
{
607626
const { renderedComponents } = await takeRender();
@@ -682,7 +701,7 @@ test("parses scalar fields in the result and serializes them in the error with a
682701
});
683702

684703
using _disabledAct = disableActEnvironment();
685-
const { takeRender, getCurrentSnapshot } = await renderHook(
704+
const { takeRender, loadQuery } = await renderHook(
686705
() => useLoadableQuery(query, { errorPolicy: "all" }),
687706
{ wrapper: createClientWrapper(client) }
688707
);
@@ -693,7 +712,7 @@ test("parses scalar fields in the result and serializes them in the error with a
693712
expect(renderedComponents).toStrictEqual(["useLoadableQuery"]);
694713
}
695714

696-
getCurrentSnapshot().loadQuery();
715+
loadQuery();
697716

698717
{
699718
const { renderedComponents } = await takeRender();
@@ -786,7 +805,7 @@ test("parses custom scalar fields with an `ignore` error policy", async () => {
786805
});
787806

788807
using _disabledAct = disableActEnvironment();
789-
const { takeRender, getCurrentSnapshot } = await renderHook(
808+
const { takeRender, loadQuery } = await renderHook(
790809
() => useLoadableQuery(query, { errorPolicy: "ignore" }),
791810
{ wrapper: createClientWrapper(client) }
792811
);
@@ -797,7 +816,7 @@ test("parses custom scalar fields with an `ignore` error policy", async () => {
797816
expect(renderedComponents).toStrictEqual(["useLoadableQuery"]);
798817
}
799818

800-
getCurrentSnapshot().loadQuery();
819+
loadQuery();
801820

802821
{
803822
const { renderedComponents } = await takeRender();

0 commit comments

Comments
 (0)