Skip to content

Commit aae4088

Browse files
authored
Merge branch 'release-3.8' into return-networkstatus-from-usesuspensequery
2 parents 7a2b00d + 54c4d2f commit aae4088

File tree

3 files changed

+103
-25
lines changed

3 files changed

+103
-25
lines changed

.changeset/silver-radios-chew.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@apollo/client': patch
3+
---
4+
5+
Ensure `refetch`, `fetchMore`, and `subscribeToMore` functions returned by `useSuspenseQuery` are referentially stable between renders, even as `data` is updated.

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

+37
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,43 @@ describe('useSuspenseQuery', () => {
504504
expect(result.current).toBe(previousResult);
505505
});
506506

507+
it('ensures refetch, fetchMore, and subscribeToMore are referentially stable even after result data has changed', async () => {
508+
const { query, mocks } = useSimpleQueryCase();
509+
510+
const client = new ApolloClient({
511+
link: new MockLink(mocks),
512+
cache: new InMemoryCache(),
513+
});
514+
515+
const { result } = renderSuspenseHook(() => useSuspenseQuery(query), {
516+
client,
517+
});
518+
519+
await waitFor(() => {
520+
expect(result.current).toMatchObject({
521+
...mocks[0].result,
522+
error: undefined,
523+
});
524+
});
525+
526+
const previousResult = result.current;
527+
528+
client.writeQuery({
529+
query,
530+
data: { greeting: 'Updated cache greeting' },
531+
});
532+
533+
await waitFor(() => {
534+
expect(result.current.data).toEqual({
535+
greeting: 'Updated cache greeting',
536+
});
537+
});
538+
539+
expect(result.current.fetchMore).toBe(previousResult.fetchMore);
540+
expect(result.current.refetch).toBe(previousResult.refetch);
541+
expect(result.current.subscribeToMore).toBe(previousResult.subscribeToMore);
542+
});
543+
507544
it('enables canonical results when canonizeResults is "true"', async () => {
508545
interface Result {
509546
__typename: string;

src/react/hooks/useSuspenseQuery.ts

+61-25
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,27 @@ export interface UseSuspenseQueryResult<
3737
client: ApolloClient<any>;
3838
data: TData;
3939
error: ApolloError | undefined;
40-
fetchMore: ObservableQueryFields<TData, TVariables>['fetchMore'];
40+
fetchMore: FetchMoreFunction<TData, TVariables>;
4141
networkStatus: NetworkStatus;
42-
refetch: ObservableQueryFields<TData, TVariables>['refetch'];
43-
subscribeToMore: ObservableQueryFields<TData, TVariables>['subscribeToMore'];
42+
refetch: RefetchFunction<TData, TVariables>;
43+
subscribeToMore: SubscribeToMoreFunction<TData, TVariables>;
4444
}
4545

46+
type FetchMoreFunction<
47+
TData,
48+
TVariables extends OperationVariables
49+
> = ObservableQueryFields<TData, TVariables>['fetchMore'];
50+
51+
type RefetchFunction<
52+
TData,
53+
TVariables extends OperationVariables
54+
> = ObservableQueryFields<TData, TVariables>['refetch'];
55+
56+
type SubscribeToMoreFunction<
57+
TData,
58+
TVariables extends OperationVariables
59+
> = ObservableQueryFields<TData, TVariables>['subscribeToMore'];
60+
4661
const SUPPORTED_FETCH_POLICIES: WatchQueryFetchPolicy[] = [
4762
'cache-first',
4863
'network-only',
@@ -148,35 +163,56 @@ export function useSuspenseQuery_experimental<
148163
};
149164
}, []);
150165

166+
const fetchMore: FetchMoreFunction<TData, TVariables> = useCallback(
167+
(options) => {
168+
const promise = observable.fetchMore(options);
169+
170+
suspenseCache.add(query, watchQueryOptions.variables, {
171+
promise,
172+
observable,
173+
});
174+
175+
return promise;
176+
},
177+
[observable]
178+
);
179+
180+
const refetch: RefetchFunction<TData, TVariables> = useCallback(
181+
(variables) => {
182+
const promise = observable.refetch(variables);
183+
184+
suspenseCache.add(query, watchQueryOptions.variables, {
185+
promise,
186+
observable,
187+
});
188+
189+
return promise;
190+
},
191+
[observable]
192+
);
193+
194+
const subscribeToMore: SubscribeToMoreFunction<TData, TVariables> =
195+
useCallback((options) => observable.subscribeToMore(options), [observable]);
196+
151197
return useMemo(() => {
152198
return {
153199
client,
154200
data: result.data,
155201
error: errorPolicy === 'ignore' ? void 0 : toApolloError(result),
156202
networkStatus: result.networkStatus,
157-
fetchMore: (options) => {
158-
const promise = observable.fetchMore(options);
159-
160-
suspenseCache.add(query, watchQueryOptions.variables, {
161-
promise,
162-
observable,
163-
});
164-
165-
return promise;
166-
},
167-
refetch: (variables?: Partial<TVariables>) => {
168-
const promise = observable.refetch(variables);
169-
170-
suspenseCache.add(query, watchQueryOptions.variables, {
171-
promise,
172-
observable,
173-
});
174-
175-
return promise;
176-
},
177-
subscribeToMore: (options) => observable.subscribeToMore(options),
203+
fetchMore,
204+
refetch,
205+
subscribeToMore,
178206
};
179-
}, [client, result, observable, errorPolicy]);
207+
}, [
208+
client,
209+
fetchMore,
210+
refetch,
211+
result,
212+
observable,
213+
errorPolicy,
214+
subscribeToMore,
215+
]);
180216
}
181217

182218
function validateOptions(options: WatchQueryOptions) {

0 commit comments

Comments
 (0)