Skip to content

Commit 642092c

Browse files
authored
Fix issue where setting defaultOptions would sometimes break startTransition for suspense hooks (#11713)
1 parent 8619bc7 commit 642092c

File tree

5 files changed

+430
-1
lines changed

5 files changed

+430
-1
lines changed

.changeset/tasty-hotels-press.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@apollo/client": patch
3+
---
4+
5+
Fix issue where setting a default `watchQuery` option in the `ApolloClient` constructor could break `startTransition` when used with suspense hooks.

.size-limits.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"dist/apollo-client.min.cjs": 39273,
2+
"dist/apollo-client.min.cjs": 39277,
33
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32630
44
}

src/react/hooks/__tests__/useBackgroundQuery.test.tsx

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5249,6 +5249,210 @@ describe("fetchMore", () => {
52495249

52505250
await expect(Profiler).not.toRerender();
52515251
});
5252+
5253+
// https://github.com/apollographql/apollo-client/issues/11708
5254+
it("`fetchMore` works with startTransition when setting errorPolicy as default option in ApolloClient constructor", async () => {
5255+
type Variables = {
5256+
offset: number;
5257+
};
5258+
5259+
interface Todo {
5260+
__typename: "Todo";
5261+
id: string;
5262+
name: string;
5263+
completed: boolean;
5264+
}
5265+
interface Data {
5266+
todos: Todo[];
5267+
}
5268+
const user = userEvent.setup();
5269+
5270+
const query: TypedDocumentNode<Data, Variables> = gql`
5271+
query TodosQuery($offset: Int!) {
5272+
todos(offset: $offset) {
5273+
id
5274+
name
5275+
completed
5276+
}
5277+
}
5278+
`;
5279+
5280+
const mocks: MockedResponse<Data, Variables>[] = [
5281+
{
5282+
request: { query, variables: { offset: 0 } },
5283+
result: {
5284+
data: {
5285+
todos: [
5286+
{
5287+
__typename: "Todo",
5288+
id: "1",
5289+
name: "Clean room",
5290+
completed: false,
5291+
},
5292+
],
5293+
},
5294+
},
5295+
delay: 10,
5296+
},
5297+
{
5298+
request: { query, variables: { offset: 1 } },
5299+
result: {
5300+
data: {
5301+
todos: [
5302+
{
5303+
__typename: "Todo",
5304+
id: "2",
5305+
name: "Take out trash",
5306+
completed: true,
5307+
},
5308+
],
5309+
},
5310+
},
5311+
delay: 10,
5312+
},
5313+
];
5314+
5315+
const Profiler = createProfiler({
5316+
initialSnapshot: {
5317+
isPending: false,
5318+
result: null as UseReadQueryResult<Data> | null,
5319+
},
5320+
});
5321+
5322+
const { SuspenseFallback, ReadQueryHook } =
5323+
createDefaultTrackedComponents(Profiler);
5324+
5325+
const client = new ApolloClient({
5326+
link: new MockLink(mocks),
5327+
cache: new InMemoryCache({
5328+
typePolicies: {
5329+
Query: {
5330+
fields: {
5331+
todos: offsetLimitPagination(),
5332+
},
5333+
},
5334+
},
5335+
}),
5336+
defaultOptions: {
5337+
watchQuery: {
5338+
errorPolicy: "all",
5339+
},
5340+
},
5341+
});
5342+
5343+
function App() {
5344+
useTrackRenders();
5345+
const [isPending, startTransition] = React.useTransition();
5346+
const [queryRef, { fetchMore }] = useBackgroundQuery(query, {
5347+
variables: { offset: 0 },
5348+
});
5349+
5350+
Profiler.mergeSnapshot({ isPending });
5351+
5352+
return (
5353+
<>
5354+
<button
5355+
onClick={() => {
5356+
startTransition(() => {
5357+
fetchMore({ variables: { offset: 1 } });
5358+
});
5359+
}}
5360+
>
5361+
Load more
5362+
</button>
5363+
<Suspense fallback={<SuspenseFallback />}>
5364+
<ReadQueryHook queryRef={queryRef} />
5365+
</Suspense>
5366+
</>
5367+
);
5368+
}
5369+
5370+
renderWithClient(<App />, { client, wrapper: Profiler });
5371+
5372+
{
5373+
const { renderedComponents } = await Profiler.takeRender();
5374+
5375+
expect(renderedComponents).toStrictEqual([App, SuspenseFallback]);
5376+
}
5377+
5378+
{
5379+
const { snapshot } = await Profiler.takeRender();
5380+
5381+
expect(snapshot).toEqual({
5382+
isPending: false,
5383+
result: {
5384+
data: {
5385+
todos: [
5386+
{
5387+
__typename: "Todo",
5388+
id: "1",
5389+
name: "Clean room",
5390+
completed: false,
5391+
},
5392+
],
5393+
},
5394+
error: undefined,
5395+
networkStatus: NetworkStatus.ready,
5396+
},
5397+
});
5398+
}
5399+
5400+
await act(() => user.click(screen.getByText("Load more")));
5401+
5402+
{
5403+
const { snapshot, renderedComponents } = await Profiler.takeRender();
5404+
5405+
expect(renderedComponents).toStrictEqual([App, ReadQueryHook]);
5406+
expect(snapshot).toEqual({
5407+
isPending: true,
5408+
result: {
5409+
data: {
5410+
todos: [
5411+
{
5412+
__typename: "Todo",
5413+
id: "1",
5414+
name: "Clean room",
5415+
completed: false,
5416+
},
5417+
],
5418+
},
5419+
error: undefined,
5420+
networkStatus: NetworkStatus.ready,
5421+
},
5422+
});
5423+
}
5424+
5425+
{
5426+
const { snapshot, renderedComponents } = await Profiler.takeRender();
5427+
5428+
expect(renderedComponents).toStrictEqual([App, ReadQueryHook]);
5429+
expect(snapshot).toEqual({
5430+
isPending: false,
5431+
result: {
5432+
data: {
5433+
todos: [
5434+
{
5435+
__typename: "Todo",
5436+
id: "1",
5437+
name: "Clean room",
5438+
completed: false,
5439+
},
5440+
{
5441+
__typename: "Todo",
5442+
id: "2",
5443+
name: "Take out trash",
5444+
completed: true,
5445+
},
5446+
],
5447+
},
5448+
error: undefined,
5449+
networkStatus: NetworkStatus.ready,
5450+
},
5451+
});
5452+
}
5453+
5454+
await expect(Profiler).not.toRerender();
5455+
});
52525456
});
52535457

52545458
describe.skip("type tests", () => {

0 commit comments

Comments
 (0)