diff --git a/.api-reports/api-report-react.md b/.api-reports/api-report-react.md index 0b90201df27..4b531996957 100644 --- a/.api-reports/api-report-react.md +++ b/.api-reports/api-report-react.md @@ -512,13 +512,6 @@ namespace Cache_2 { import Fragment = DataProxy.Fragment; } -// @public (undocumented) -type CacheKey = [ -query: DocumentNode, -stringifiedVariables: string, -...queryKey: any[] -]; - // @public (undocumented) const enum CacheWriteBehavior { // (undocumented) @@ -916,10 +909,10 @@ class InternalQueryReference { // // (undocumented) fetchMore(options: FetchMoreOptions): Promise>; - // Warning: (ae-forgotten-export) The symbol "CacheKey" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "QueryKey" needs to be exported by the entry point index.d.ts // // (undocumented) - readonly key: CacheKey; + readonly key: QueryKey; // Warning: (ae-forgotten-export) The symbol "Listener" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -929,7 +922,7 @@ class InternalQueryReference { // (undocumented) promise: Promise>; // (undocumented) - promiseCache?: Map>>; + promiseCache?: Map>>; // (undocumented) refetch(variables: OperationVariables | undefined): Promise>; // (undocumented) @@ -945,8 +938,6 @@ interface InternalQueryReferenceOptions { // (undocumented) autoDisposeTimeoutMs?: number; // (undocumented) - key: CacheKey; - // (undocumented) onDispose?: () => void; } @@ -1537,6 +1528,12 @@ class QueryInfo { variables?: Record; } +// @public (undocumented) +interface QueryKey { + // (undocumented) + __queryKey?: string; +} + // @public (undocumented) export interface QueryLazyOptions { // (undocumented) diff --git a/.api-reports/api-report-react_hooks.md b/.api-reports/api-report-react_hooks.md index 5e17fe31a09..cbeb6aa48a7 100644 --- a/.api-reports/api-report-react_hooks.md +++ b/.api-reports/api-report-react_hooks.md @@ -488,13 +488,6 @@ namespace Cache_2 { import Fragment = DataProxy.Fragment; } -// @public (undocumented) -type CacheKey = [ -query: DocumentNode, -stringifiedVariables: string, -...queryKey: any[] -]; - // @public (undocumented) const enum CacheWriteBehavior { // (undocumented) @@ -863,10 +856,10 @@ class InternalQueryReference { // // (undocumented) fetchMore(options: FetchMoreOptions): Promise>; - // Warning: (ae-forgotten-export) The symbol "CacheKey" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "QueryKey" needs to be exported by the entry point index.d.ts // // (undocumented) - readonly key: CacheKey; + readonly key: QueryKey; // Warning: (ae-forgotten-export) The symbol "Listener" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -876,7 +869,7 @@ class InternalQueryReference { // (undocumented) promise: Promise>; // (undocumented) - promiseCache?: Map>>; + promiseCache?: Map>>; // (undocumented) refetch(variables: OperationVariables | undefined): Promise>; // (undocumented) @@ -892,8 +885,6 @@ interface InternalQueryReferenceOptions { // (undocumented) autoDisposeTimeoutMs?: number; // (undocumented) - key: CacheKey; - // (undocumented) onDispose?: () => void; } @@ -1464,6 +1455,12 @@ class QueryInfo { variables?: Record; } +// @public (undocumented) +interface QueryKey { + // (undocumented) + __queryKey?: string; +} + // @public (undocumented) type QueryListener = (queryInfo: QueryInfo) => void; diff --git a/.api-reports/api-report.md b/.api-reports/api-report.md index 04a979da240..1b63551c899 100644 --- a/.api-reports/api-report.md +++ b/.api-reports/api-report.md @@ -502,13 +502,6 @@ class CacheGroup { resetCaching(): void; } -// @public (undocumented) -type CacheKey = [ -query: DocumentNode, -stringifiedVariables: string, -...queryKey: any[] -]; - // @public (undocumented) const enum CacheWriteBehavior { // (undocumented) @@ -1275,10 +1268,10 @@ class InternalQueryReference { // // (undocumented) fetchMore(options: FetchMoreOptions_2): Promise>; - // Warning: (ae-forgotten-export) The symbol "CacheKey" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "QueryKey" needs to be exported by the entry point index.d.ts // // (undocumented) - readonly key: CacheKey; + readonly key: QueryKey; // Warning: (ae-forgotten-export) The symbol "Listener" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -1288,7 +1281,7 @@ class InternalQueryReference { // (undocumented) promise: Promise>; // (undocumented) - promiseCache?: Map>>; + promiseCache?: Map>>; // (undocumented) refetch(variables: OperationVariables | undefined): Promise>; // (undocumented) @@ -1304,8 +1297,6 @@ interface InternalQueryReferenceOptions { // (undocumented) autoDisposeTimeoutMs?: number; // (undocumented) - key: CacheKey; - // (undocumented) onDispose?: () => void; } @@ -2093,6 +2084,12 @@ class QueryInfo { variables?: Record; } +// @public (undocumented) +interface QueryKey { + // (undocumented) + __queryKey?: string; +} + // @public (undocumented) export interface QueryLazyOptions { // (undocumented) diff --git a/.size-limits.json b/.size-limits.json index dfe8306d94f..dbf215c6ea5 100644 --- a/.size-limits.json +++ b/.size-limits.json @@ -1,4 +1,4 @@ { - "dist/apollo-client.min.cjs": 38693, + "dist/apollo-client.min.cjs": 38675, "import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32306 } diff --git a/src/react/cache/QueryReference.ts b/src/react/cache/QueryReference.ts index 5b2fc14c643..7da866b5309 100644 --- a/src/react/cache/QueryReference.ts +++ b/src/react/cache/QueryReference.ts @@ -12,7 +12,7 @@ import { createFulfilledPromise, createRejectedPromise, } from "../../utilities/index.js"; -import type { CacheKey } from "./types.js"; +import type { QueryKey } from "./types.js"; import type { useBackgroundQuery, useReadQuery } from "../hooks/index.js"; type Listener = (promise: Promise>) => void; @@ -32,7 +32,6 @@ export interface QueryReference { } interface InternalQueryReferenceOptions { - key: CacheKey; onDispose?: () => void; autoDisposeTimeoutMs?: number; } @@ -65,10 +64,10 @@ type ObservedOptions = Pick< export class InternalQueryReference { public result: ApolloQueryResult; - public readonly key: CacheKey; + public readonly key: QueryKey = {}; public readonly observable: ObservableQuery; - public promiseCache?: Map>>; + public promiseCache?: Map>>; public promise: Promise>; private subscription: ObservableSubscription; @@ -92,7 +91,6 @@ export class InternalQueryReference { // Don't save this result as last result to prevent delivery of last result // when first subscribing this.result = observable.getCurrentResult(false); - this.key = options.key; if (options.onDispose) { this.onDispose = options.onDispose; diff --git a/src/react/cache/SuspenseCache.ts b/src/react/cache/SuspenseCache.ts index af883c0a52a..36641c76cb4 100644 --- a/src/react/cache/SuspenseCache.ts +++ b/src/react/cache/SuspenseCache.ts @@ -38,7 +38,6 @@ export class SuspenseCache { if (!ref.current) { ref.current = new InternalQueryReference(createObservable(), { - key: cacheKey, autoDisposeTimeoutMs: this.options.autoDisposeTimeoutMs, onDispose: () => { delete ref.current; diff --git a/src/react/cache/types.ts b/src/react/cache/types.ts index e9e0ba8b826..40f3c4cc8fc 100644 --- a/src/react/cache/types.ts +++ b/src/react/cache/types.ts @@ -5,3 +5,7 @@ export type CacheKey = [ stringifiedVariables: string, ...queryKey: any[], ]; + +export interface QueryKey { + __queryKey?: string; +} diff --git a/src/react/hooks/__tests__/useSuspenseQuery.test.tsx b/src/react/hooks/__tests__/useSuspenseQuery.test.tsx index 642be7d023a..86af1878883 100644 --- a/src/react/hooks/__tests__/useSuspenseQuery.test.tsx +++ b/src/react/hooks/__tests__/useSuspenseQuery.test.tsx @@ -3650,8 +3650,7 @@ describe("useSuspenseQuery", () => { }); await waitFor(() => expect(renders.errorCount).toBe(1)); - - expect(client.getObservableQueries().size).toBe(0); + await waitFor(() => expect(client.getObservableQueries().size).toBe(0)); }); it('throws network errors when errorPolicy is set to "none"', async () => { diff --git a/src/react/hooks/useSuspenseQuery.ts b/src/react/hooks/useSuspenseQuery.ts index ed203d4cfee..8de82bf471e 100644 --- a/src/react/hooks/useSuspenseQuery.ts +++ b/src/react/hooks/useSuspenseQuery.ts @@ -25,7 +25,7 @@ import { getSuspenseCache } from "../cache/index.js"; import { canonicalStringify } from "../../cache/index.js"; import { skipToken } from "./constants.js"; import type { SkipToken } from "./constants.js"; -import type { CacheKey } from "../cache/types.js"; +import type { CacheKey, QueryKey } from "../cache/types.js"; export interface UseSuspenseQueryResult< TData = unknown, @@ -196,29 +196,26 @@ export function useSuspenseQuery< client.watchQuery(watchQueryOptions) ); - const [promiseCache, setPromiseCache] = React.useState( - () => new Map([[queryRef.key, queryRef.promise]]) - ); - - let promise = promiseCache.get(queryRef.key); + let [current, setPromise] = React.useState< + [QueryKey, Promise>] + >([queryRef.key, queryRef.promise]); - if (queryRef.didChangeOptions(watchQueryOptions)) { - promise = queryRef.applyOptions(watchQueryOptions); - promiseCache.set(queryRef.key, promise); + // This saves us a re-execution of the render function when a variable changed. + if (current[0] !== queryRef.key) { + current[0] = queryRef.key; + current[1] = queryRef.promise; } + let promise = current[1]; - if (!promise) { - promise = queryRef.promise; - promiseCache.set(queryRef.key, promise); + if (queryRef.didChangeOptions(watchQueryOptions)) { + current[1] = promise = queryRef.applyOptions(watchQueryOptions); } React.useEffect(() => { const dispose = queryRef.retain(); const removeListener = queryRef.listen((promise) => { - setPromiseCache((promiseCache) => - new Map(promiseCache).set(queryRef.key, promise) - ); + setPromise([queryRef.key, promise]); }); return () => { @@ -239,14 +236,10 @@ export function useSuspenseQuery< }, [queryRef.result]); const result = fetchPolicy === "standby" ? skipResult : __use(promise); - const fetchMore = React.useCallback( ((options) => { const promise = queryRef.fetchMore(options); - - setPromiseCache((previousPromiseCache) => - new Map(previousPromiseCache).set(queryRef.key, queryRef.promise) - ); + setPromise([queryRef.key, queryRef.promise]); return promise; }) satisfies FetchMoreFunction< @@ -259,10 +252,7 @@ export function useSuspenseQuery< const refetch: RefetchFunction = React.useCallback( (variables) => { const promise = queryRef.refetch(variables); - - setPromiseCache((previousPromiseCache) => - new Map(previousPromiseCache).set(queryRef.key, queryRef.promise) - ); + setPromise([queryRef.key, queryRef.promise]); return promise; },