From c3d9abfb653545a345daf3ba8d4c7c67f8126831 Mon Sep 17 00:00:00 2001 From: Jerel Miller <jerelmiller@gmail.com> Date: Tue, 30 Apr 2024 17:13:02 -0600 Subject: [PATCH 01/10] Add failing test for useBackgroundQuery issue --- .../__tests__/useBackgroundQuery.test.tsx | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/react/hooks/__tests__/useBackgroundQuery.test.tsx b/src/react/hooks/__tests__/useBackgroundQuery.test.tsx index 605c5ed42c1..36fa009ba73 100644 --- a/src/react/hooks/__tests__/useBackgroundQuery.test.tsx +++ b/src/react/hooks/__tests__/useBackgroundQuery.test.tsx @@ -536,6 +536,88 @@ it("does not recreate queryRef and execute a network request when rerendering us await expect(Profiler).not.toRerender({ timeout: 50 }); }); +// https://github.com/apollographql/apollo-client/issues/11815 +it("does not recreate queryRef or execute a network request when rerendering useBackgroundQuery in strict mode", async () => { + const { query } = setupSimpleCase(); + const user = userEvent.setup(); + let fetchCount = 0; + const client = new ApolloClient({ + link: new ApolloLink(() => { + fetchCount++; + + return new Observable((observer) => { + setTimeout(() => { + observer.next({ data: { greeting: "Hello" } }); + observer.complete(); + }, 20); + }); + }), + cache: new InMemoryCache(), + }); + + const Profiler = createProfiler({ + initialSnapshot: { + queryRef: null as QueryReference<SimpleCaseData> | null, + result: null as UseReadQueryResult<SimpleCaseData> | null, + }, + }); + const { SuspenseFallback, ReadQueryHook } = + createDefaultTrackedComponents(Profiler); + + function App() { + useTrackRenders(); + const [, setCount] = React.useState(0); + // Use a fetchPolicy of no-cache to ensure we can more easily track if + // another network request was made + const [queryRef] = useBackgroundQuery(query, { fetchPolicy: "no-cache" }); + + Profiler.mergeSnapshot({ queryRef }); + + return ( + <> + <button onClick={() => setCount((count) => count + 1)}> + Increment + </button> + <Suspense fallback={<SuspenseFallback />}> + <ReadQueryHook queryRef={queryRef} /> + </Suspense> + </> + ); + } + + renderWithClient(<App />, { + client, + wrapper: ({ children }) => ( + <Profiler> + <React.StrictMode>{children}</React.StrictMode> + </Profiler> + ), + }); + + const incrementButton = screen.getByText("Increment"); + + { + const { renderedComponents } = await Profiler.takeRender(); + + expect(renderedComponents).toStrictEqual([App, SuspenseFallback]); + } + + // eslint-disable-next-line testing-library/render-result-naming-convention + const firstRender = await Profiler.takeRender(); + const initialQueryRef = firstRender.snapshot.queryRef; + + await act(() => user.click(incrementButton)); + + { + const { snapshot } = await Profiler.takeRender(); + + expect(snapshot.queryRef).toBe(initialQueryRef); + expect(fetchCount).toBe(1); + } + + await expect(Profiler).not.toRerender({ timeout: 50 }); +}); + it("disposes of the queryRef when unmounting before it is used by useReadQuery", async () => { const { query, mocks } = setupSimpleCase(); const client = new ApolloClient({ From 2f997d2f7a38588b14eefc5d85e7cce6b1b78342 Mon Sep 17 00:00:00 2001 From: Jerel Miller <jerelmiller@gmail.com> Date: Tue, 30 Apr 2024 17:48:09 -0600 Subject: [PATCH 02/10] Fix issue where queryRef is disposed in useReadQuery and not added back to Suspense cache in strict mode --- src/react/hooks/useBackgroundQuery.ts | 2 +- src/react/hooks/useReadQuery.ts | 1 + src/react/hooks/useSuspenseQuery.ts | 2 +- src/react/internal/cache/QueryReference.ts | 9 +++++++++ src/react/internal/cache/SuspenseCache.ts | 9 ++++----- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/react/hooks/useBackgroundQuery.ts b/src/react/hooks/useBackgroundQuery.ts index 0b060b2476f..e07ecac873a 100644 --- a/src/react/hooks/useBackgroundQuery.ts +++ b/src/react/hooks/useBackgroundQuery.ts @@ -247,7 +247,7 @@ function _useBackgroundQuery< // useReadQuery will ensure the same queryRef is maintained. React.useEffect(() => { if (queryRef.disposed) { - suspenseCache.add(cacheKey, queryRef); + queryRef.strictModeFixAddToSuspenseCache(); } // Omitting the deps is intentional. This avoids stale closures and the // conditional ensures we aren't running the logic on each render. diff --git a/src/react/hooks/useReadQuery.ts b/src/react/hooks/useReadQuery.ts index 6add98a094b..681a40660a4 100644 --- a/src/react/hooks/useReadQuery.ts +++ b/src/react/hooks/useReadQuery.ts @@ -89,6 +89,7 @@ function _useReadQuery<TData>( // handle strict mode where this useEffect will be run twice resulting in a // disposed queryRef before the next render. if (internalQueryRef.disposed) { + internalQueryRef.strictModeFixAddToSuspenseCache(); internalQueryRef.reinitialize(); } diff --git a/src/react/hooks/useSuspenseQuery.ts b/src/react/hooks/useSuspenseQuery.ts index 0459aba5087..e6b252d470f 100644 --- a/src/react/hooks/useSuspenseQuery.ts +++ b/src/react/hooks/useSuspenseQuery.ts @@ -258,7 +258,7 @@ function _useSuspenseQuery< // We address this by adding the queryRef back to the suspense cache // so that the lookup on the next render uses the existing queryRef rather // than instantiating a new one. - suspenseCache.add(cacheKey, queryRef); + queryRef.strictModeFixAddToSuspenseCache(); queryRef.reinitialize(); } // We can omit the deps here to get a fresh closure each render since the diff --git a/src/react/internal/cache/QueryReference.ts b/src/react/internal/cache/QueryReference.ts index f3bd6395b8c..2f933526119 100644 --- a/src/react/internal/cache/QueryReference.ts +++ b/src/react/internal/cache/QueryReference.ts @@ -80,6 +80,7 @@ export interface QueryReference<TData = unknown, TVariables = unknown> { interface InternalQueryReferenceOptions { onDispose?: () => void; + addToSuspenseCache?: (queryRef: InternalQueryReference<any>) => void; autoDisposeTimeoutMs?: number; } @@ -147,6 +148,9 @@ export class InternalQueryReference<TData = unknown> { public result!: ApolloQueryResult<TData>; public readonly key: QueryKey = {}; public readonly observable: ObservableQuery<TData>; + private readonly addToSuspenseCache?: ( + queryRef: InternalQueryReference<TData> + ) => void; public promise!: QueryRefPromise<TData>; @@ -168,6 +172,7 @@ export class InternalQueryReference<TData = unknown> { this.handleError = this.handleError.bind(this); this.dispose = this.dispose.bind(this); this.observable = observable; + this.addToSuspenseCache = options.addToSuspenseCache; if (options.onDispose) { this.onDispose = options.onDispose; @@ -203,6 +208,10 @@ export class InternalQueryReference<TData = unknown> { return this.observable.options; } + strictModeFixAddToSuspenseCache() { + this.addToSuspenseCache?.(this); + } + reinitialize() { const { observable } = this; diff --git a/src/react/internal/cache/SuspenseCache.ts b/src/react/internal/cache/SuspenseCache.ts index 8b1eba321b5..4ef90f18485 100644 --- a/src/react/internal/cache/SuspenseCache.ts +++ b/src/react/internal/cache/SuspenseCache.ts @@ -39,6 +39,10 @@ export class SuspenseCache { if (!ref.current) { ref.current = new InternalQueryReference(createObservable(), { autoDisposeTimeoutMs: this.options.autoDisposeTimeoutMs, + addToSuspenseCache: (queryRef) => { + const ref = this.queryRefs.lookupArray(cacheKey); + ref.current = queryRef; + }, onDispose: () => { delete ref.current; }, @@ -47,9 +51,4 @@ export class SuspenseCache { return ref.current; } - - add(cacheKey: CacheKey, queryRef: InternalQueryReference<unknown>) { - const ref = this.queryRefs.lookupArray(cacheKey); - ref.current = queryRef; - } } From 5b4e5c050abe30a7fa2a74323012e3b9870fe805 Mon Sep 17 00:00:00 2001 From: Jerel Miller <jerelmiller@gmail.com> Date: Tue, 30 Apr 2024 17:50:47 -0600 Subject: [PATCH 03/10] Add changeset --- .changeset/lazy-parents-thank.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/lazy-parents-thank.md diff --git a/.changeset/lazy-parents-thank.md b/.changeset/lazy-parents-thank.md new file mode 100644 index 00000000000..7e35d83112a --- /dev/null +++ b/.changeset/lazy-parents-thank.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": patch +--- + +Fix a regression where rerendering a component with `useBackgroundQuery` would recreate the `queryRef` instance when used with React's strict mode. From 24d89ca8f9c200a04eccbf75724c5472bf86a0b7 Mon Sep 17 00:00:00 2001 From: Jerel Miller <jerelmiller@gmail.com> Date: Tue, 30 Apr 2024 17:57:23 -0600 Subject: [PATCH 04/10] Update API report --- .api-reports/api-report-react.md | 8 ++++++-- .api-reports/api-report-react_hooks.md | 8 ++++++-- .api-reports/api-report-react_internal.md | 6 ++++-- .api-reports/api-report.md | 8 ++++++-- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.api-reports/api-report-react.md b/.api-reports/api-report-react.md index 9058ec39286..501d03ad943 100644 --- a/.api-reports/api-report-react.md +++ b/.api-reports/api-report-react.md @@ -959,11 +959,17 @@ class InternalQueryReference<TData = unknown> { // (undocumented) softRetain(): () => void; // (undocumented) + strictModeFixAddToSuspenseCache(): void; + // (undocumented) get watchQueryOptions(): WatchQueryOptions<OperationVariables, TData>; } // @public (undocumented) interface InternalQueryReferenceOptions { + // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts + // + // (undocumented) + addToSuspenseCache?: (queryRef: InternalQueryReference<any>) => void; // (undocumented) autoDisposeTimeoutMs?: number; // (undocumented) @@ -1756,8 +1762,6 @@ interface QueryOptions<TVariables = OperationVariables, TData = any> { export interface QueryReference<TData = unknown, TVariables = unknown> { // @internal (undocumented) [PROMISE_SYMBOL]: QueryRefPromise<TData>; - // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts - // // @internal (undocumented) readonly [QUERY_REFERENCE_SYMBOL]: InternalQueryReference<TData>; toPromise(): Promise<QueryReference<TData, TVariables>>; diff --git a/.api-reports/api-report-react_hooks.md b/.api-reports/api-report-react_hooks.md index e5ba909b727..71e5af5477b 100644 --- a/.api-reports/api-report-react_hooks.md +++ b/.api-reports/api-report-react_hooks.md @@ -901,11 +901,17 @@ class InternalQueryReference<TData = unknown> { // (undocumented) softRetain(): () => void; // (undocumented) + strictModeFixAddToSuspenseCache(): void; + // (undocumented) get watchQueryOptions(): WatchQueryOptions<OperationVariables, TData>; } // @public (undocumented) interface InternalQueryReferenceOptions { + // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts + // + // (undocumented) + addToSuspenseCache?: (queryRef: InternalQueryReference<any>) => void; // (undocumented) autoDisposeTimeoutMs?: number; // (undocumented) @@ -1631,8 +1637,6 @@ interface QueryOptions<TVariables = OperationVariables, TData = any> { interface QueryReference<TData = unknown, TVariables = unknown> { // @internal (undocumented) [PROMISE_SYMBOL]: QueryRefPromise<TData>; - // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts - // // @internal (undocumented) readonly [QUERY_REFERENCE_SYMBOL]: InternalQueryReference<TData>; toPromise(): Promise<QueryReference<TData, TVariables>>; diff --git a/.api-reports/api-report-react_internal.md b/.api-reports/api-report-react_internal.md index 69aa5c05e09..22031bad0cf 100644 --- a/.api-reports/api-report-react_internal.md +++ b/.api-reports/api-report-react_internal.md @@ -891,11 +891,15 @@ export class InternalQueryReference<TData = unknown> { // (undocumented) softRetain(): () => void; // (undocumented) + strictModeFixAddToSuspenseCache(): void; + // (undocumented) get watchQueryOptions(): WatchQueryOptions<OperationVariables, TData>; } // @public (undocumented) interface InternalQueryReferenceOptions { + // (undocumented) + addToSuspenseCache?: (queryRef: InternalQueryReference<any>) => void; // (undocumented) autoDisposeTimeoutMs?: number; // (undocumented) @@ -1737,8 +1741,6 @@ interface SubscriptionOptions<TVariables = OperationVariables, TData = any> { class SuspenseCache { constructor(options?: SuspenseCacheOptions); // (undocumented) - add(cacheKey: CacheKey, queryRef: InternalQueryReference<unknown>): void; - // (undocumented) getQueryRef<TData = any>(cacheKey: CacheKey, createObservable: () => ObservableQuery<TData>): InternalQueryReference<TData>; } diff --git a/.api-reports/api-report.md b/.api-reports/api-report.md index 9b72b6ae88b..5bd4088b017 100644 --- a/.api-reports/api-report.md +++ b/.api-reports/api-report.md @@ -1332,11 +1332,17 @@ class InternalQueryReference<TData = unknown> { // (undocumented) softRetain(): () => void; // (undocumented) + strictModeFixAddToSuspenseCache(): void; + // (undocumented) get watchQueryOptions(): WatchQueryOptions<OperationVariables, TData>; } // @public (undocumented) interface InternalQueryReferenceOptions { + // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts + // + // (undocumented) + addToSuspenseCache?: (queryRef: InternalQueryReference<any>) => void; // (undocumented) autoDisposeTimeoutMs?: number; // (undocumented) @@ -2327,8 +2333,6 @@ export { QueryOptions } export interface QueryReference<TData = unknown, TVariables = unknown> { // @internal (undocumented) [PROMISE_SYMBOL]: QueryRefPromise<TData>; - // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts - // // @internal (undocumented) readonly [QUERY_REFERENCE_SYMBOL]: InternalQueryReference<TData>; toPromise(): Promise<QueryReference<TData, TVariables>>; From 38efc53ace45290001a9ab48dac6250a468d21c8 Mon Sep 17 00:00:00 2001 From: Jerel Miller <jerelmiller@gmail.com> Date: Tue, 30 Apr 2024 17:57:39 -0600 Subject: [PATCH 05/10] Update size limits --- .size-limits.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.size-limits.json b/.size-limits.json index b18103fdaec..45dcdd441b8 100644 --- a/.size-limits.json +++ b/.size-limits.json @@ -1,4 +1,4 @@ { - "dist/apollo-client.min.cjs": 39551, + "dist/apollo-client.min.cjs": 39614, "import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32826 } From c4bf002d483f4e0c99de6fa2eacd06c84c8cc0a8 Mon Sep 17 00:00:00 2001 From: Jerel Miller <jerelmiller@gmail.com> Date: Thu, 2 May 2024 15:27:46 -0600 Subject: [PATCH 06/10] Wrap dispose check in setTimeout in useBackgroundQuery --- src/react/hooks/useBackgroundQuery.ts | 23 +++++++++++------- src/react/hooks/useReadQuery.ts | 13 +--------- src/react/hooks/useSuspenseQuery.ts | 28 ---------------------- src/react/internal/cache/QueryReference.ts | 14 ++++------- src/react/internal/cache/SuspenseCache.ts | 9 +++---- 5 files changed, 25 insertions(+), 62 deletions(-) diff --git a/src/react/hooks/useBackgroundQuery.ts b/src/react/hooks/useBackgroundQuery.ts index e07ecac873a..601273ce627 100644 --- a/src/react/hooks/useBackgroundQuery.ts +++ b/src/react/hooks/useBackgroundQuery.ts @@ -239,16 +239,21 @@ function _useBackgroundQuery< updateWrappedQueryRef(wrappedQueryRef, promise); } - // Handle strict mode where the query ref might be disposed when useEffect - // runs twice. We add the queryRef back in the suspense cache so that the next - // render will reuse this queryRef rather than initializing a new instance. - // This also prevents issues where rerendering useBackgroundQuery after the - // queryRef has been disposed, either automatically or by unmounting - // useReadQuery will ensure the same queryRef is maintained. + // This prevents issues where rerendering useBackgroundQuery after the + // queryRef has been disposed would cause the hook to return a new queryRef + // instance since disposal also removes it from the suspense cache. We add + // the queryRef back in the suspense cache so that the next render will reuse + // this queryRef rather than initializing a new instance. React.useEffect(() => { - if (queryRef.disposed) { - queryRef.strictModeFixAddToSuspenseCache(); - } + // Since the queryRef is disposed async via `setTimeout`, we have to wait a + // tick before checking it and adding back to the suspense cache. + const id = setTimeout(() => { + if (queryRef.disposed) { + suspenseCache.add(cacheKey, queryRef); + } + }); + + return () => clearTimeout(id); // Omitting the deps is intentional. This avoids stale closures and the // conditional ensures we aren't running the logic on each render. }); diff --git a/src/react/hooks/useReadQuery.ts b/src/react/hooks/useReadQuery.ts index 681a40660a4..0b600c59f34 100644 --- a/src/react/hooks/useReadQuery.ts +++ b/src/react/hooks/useReadQuery.ts @@ -83,18 +83,7 @@ function _useReadQuery<TData>( updateWrappedQueryRef(queryRef, internalQueryRef.promise); } - React.useEffect(() => { - // It may seem odd that we are trying to reinitialize the queryRef even - // though we reinitialize in render above, but this is necessary to - // handle strict mode where this useEffect will be run twice resulting in a - // disposed queryRef before the next render. - if (internalQueryRef.disposed) { - internalQueryRef.strictModeFixAddToSuspenseCache(); - internalQueryRef.reinitialize(); - } - - return internalQueryRef.retain(); - }, [internalQueryRef]); + React.useEffect(() => internalQueryRef.retain(), [internalQueryRef]); const promise = useSyncExternalStore( React.useCallback( diff --git a/src/react/hooks/useSuspenseQuery.ts b/src/react/hooks/useSuspenseQuery.ts index e6b252d470f..77159eb31f2 100644 --- a/src/react/hooks/useSuspenseQuery.ts +++ b/src/react/hooks/useSuspenseQuery.ts @@ -239,34 +239,6 @@ function _useSuspenseQuery< }; }, [queryRef]); - // This effect handles the case where strict mode causes the queryRef to get - // disposed early. Previously this was done by using a `setTimeout` inside the - // dispose function, but this could cause some issues in cases where someone - // might expect the queryRef to be disposed immediately. For example, when - // using the same client instance across multiple tests in a test suite, the - // `setTimeout` has the possibility of retaining the suspense cache entry for - // too long, which means that two tests might accidentally share the same - // `queryRef` instance. By immediately disposing, we can avoid this situation. - // - // Instead we can leverage the work done to allow the queryRef to "resume" - // after it has been disposed without executing an additional network request. - // This is done by calling the `initialize` function below. - React.useEffect(() => { - if (queryRef.disposed) { - // Calling the `dispose` function removes it from the suspense cache, so - // when the component rerenders, it instantiates a fresh query ref. - // We address this by adding the queryRef back to the suspense cache - // so that the lookup on the next render uses the existing queryRef rather - // than instantiating a new one. - queryRef.strictModeFixAddToSuspenseCache(); - queryRef.reinitialize(); - } - // We can omit the deps here to get a fresh closure each render since the - // conditional will prevent the logic from running in most cases. This - // should also be a touch faster since it should be faster to check the `if` - // statement than for React to compare deps on this effect. - }); - const skipResult = React.useMemo(() => { const error = toApolloError(queryRef.result); diff --git a/src/react/internal/cache/QueryReference.ts b/src/react/internal/cache/QueryReference.ts index 2f933526119..b382b5ca221 100644 --- a/src/react/internal/cache/QueryReference.ts +++ b/src/react/internal/cache/QueryReference.ts @@ -80,7 +80,6 @@ export interface QueryReference<TData = unknown, TVariables = unknown> { interface InternalQueryReferenceOptions { onDispose?: () => void; - addToSuspenseCache?: (queryRef: InternalQueryReference<any>) => void; autoDisposeTimeoutMs?: number; } @@ -172,7 +171,6 @@ export class InternalQueryReference<TData = unknown> { this.handleError = this.handleError.bind(this); this.dispose = this.dispose.bind(this); this.observable = observable; - this.addToSuspenseCache = options.addToSuspenseCache; if (options.onDispose) { this.onDispose = options.onDispose; @@ -208,10 +206,6 @@ export class InternalQueryReference<TData = unknown> { return this.observable.options; } - strictModeFixAddToSuspenseCache() { - this.addToSuspenseCache?.(this); - } - reinitialize() { const { observable } = this; @@ -253,9 +247,11 @@ export class InternalQueryReference<TData = unknown> { disposed = true; this.references--; - if (!this.references) { - this.dispose(); - } + setTimeout(() => { + if (!this.references) { + this.dispose(); + } + }); }; } diff --git a/src/react/internal/cache/SuspenseCache.ts b/src/react/internal/cache/SuspenseCache.ts index 4ef90f18485..8b1eba321b5 100644 --- a/src/react/internal/cache/SuspenseCache.ts +++ b/src/react/internal/cache/SuspenseCache.ts @@ -39,10 +39,6 @@ export class SuspenseCache { if (!ref.current) { ref.current = new InternalQueryReference(createObservable(), { autoDisposeTimeoutMs: this.options.autoDisposeTimeoutMs, - addToSuspenseCache: (queryRef) => { - const ref = this.queryRefs.lookupArray(cacheKey); - ref.current = queryRef; - }, onDispose: () => { delete ref.current; }, @@ -51,4 +47,9 @@ export class SuspenseCache { return ref.current; } + + add(cacheKey: CacheKey, queryRef: InternalQueryReference<unknown>) { + const ref = this.queryRefs.lookupArray(cacheKey); + ref.current = queryRef; + } } From bedd9377ad3cf80f3cd0e08f0e50f39c9d55586b Mon Sep 17 00:00:00 2001 From: Jerel Miller <jerelmiller@gmail.com> Date: Thu, 2 May 2024 15:30:05 -0600 Subject: [PATCH 07/10] Remove unneeded property from QueryReference --- src/react/internal/cache/QueryReference.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/react/internal/cache/QueryReference.ts b/src/react/internal/cache/QueryReference.ts index b382b5ca221..16a014d0d89 100644 --- a/src/react/internal/cache/QueryReference.ts +++ b/src/react/internal/cache/QueryReference.ts @@ -147,9 +147,6 @@ export class InternalQueryReference<TData = unknown> { public result!: ApolloQueryResult<TData>; public readonly key: QueryKey = {}; public readonly observable: ObservableQuery<TData>; - private readonly addToSuspenseCache?: ( - queryRef: InternalQueryReference<TData> - ) => void; public promise!: QueryRefPromise<TData>; From d727d46aa91e59834e1ce40ee6c1489074422919 Mon Sep 17 00:00:00 2001 From: Jerel Miller <jerelmiller@gmail.com> Date: Thu, 2 May 2024 15:31:14 -0600 Subject: [PATCH 08/10] Rerun api report --- .api-reports/api-report-react.md | 8 ++------ .api-reports/api-report-react_hooks.md | 8 ++------ .api-reports/api-report-react_internal.md | 6 ++---- .api-reports/api-report.md | 8 ++------ 4 files changed, 8 insertions(+), 22 deletions(-) diff --git a/.api-reports/api-report-react.md b/.api-reports/api-report-react.md index 501d03ad943..9058ec39286 100644 --- a/.api-reports/api-report-react.md +++ b/.api-reports/api-report-react.md @@ -959,17 +959,11 @@ class InternalQueryReference<TData = unknown> { // (undocumented) softRetain(): () => void; // (undocumented) - strictModeFixAddToSuspenseCache(): void; - // (undocumented) get watchQueryOptions(): WatchQueryOptions<OperationVariables, TData>; } // @public (undocumented) interface InternalQueryReferenceOptions { - // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts - // - // (undocumented) - addToSuspenseCache?: (queryRef: InternalQueryReference<any>) => void; // (undocumented) autoDisposeTimeoutMs?: number; // (undocumented) @@ -1762,6 +1756,8 @@ interface QueryOptions<TVariables = OperationVariables, TData = any> { export interface QueryReference<TData = unknown, TVariables = unknown> { // @internal (undocumented) [PROMISE_SYMBOL]: QueryRefPromise<TData>; + // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts + // // @internal (undocumented) readonly [QUERY_REFERENCE_SYMBOL]: InternalQueryReference<TData>; toPromise(): Promise<QueryReference<TData, TVariables>>; diff --git a/.api-reports/api-report-react_hooks.md b/.api-reports/api-report-react_hooks.md index 71e5af5477b..e5ba909b727 100644 --- a/.api-reports/api-report-react_hooks.md +++ b/.api-reports/api-report-react_hooks.md @@ -901,17 +901,11 @@ class InternalQueryReference<TData = unknown> { // (undocumented) softRetain(): () => void; // (undocumented) - strictModeFixAddToSuspenseCache(): void; - // (undocumented) get watchQueryOptions(): WatchQueryOptions<OperationVariables, TData>; } // @public (undocumented) interface InternalQueryReferenceOptions { - // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts - // - // (undocumented) - addToSuspenseCache?: (queryRef: InternalQueryReference<any>) => void; // (undocumented) autoDisposeTimeoutMs?: number; // (undocumented) @@ -1637,6 +1631,8 @@ interface QueryOptions<TVariables = OperationVariables, TData = any> { interface QueryReference<TData = unknown, TVariables = unknown> { // @internal (undocumented) [PROMISE_SYMBOL]: QueryRefPromise<TData>; + // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts + // // @internal (undocumented) readonly [QUERY_REFERENCE_SYMBOL]: InternalQueryReference<TData>; toPromise(): Promise<QueryReference<TData, TVariables>>; diff --git a/.api-reports/api-report-react_internal.md b/.api-reports/api-report-react_internal.md index 22031bad0cf..69aa5c05e09 100644 --- a/.api-reports/api-report-react_internal.md +++ b/.api-reports/api-report-react_internal.md @@ -891,15 +891,11 @@ export class InternalQueryReference<TData = unknown> { // (undocumented) softRetain(): () => void; // (undocumented) - strictModeFixAddToSuspenseCache(): void; - // (undocumented) get watchQueryOptions(): WatchQueryOptions<OperationVariables, TData>; } // @public (undocumented) interface InternalQueryReferenceOptions { - // (undocumented) - addToSuspenseCache?: (queryRef: InternalQueryReference<any>) => void; // (undocumented) autoDisposeTimeoutMs?: number; // (undocumented) @@ -1741,6 +1737,8 @@ interface SubscriptionOptions<TVariables = OperationVariables, TData = any> { class SuspenseCache { constructor(options?: SuspenseCacheOptions); // (undocumented) + add(cacheKey: CacheKey, queryRef: InternalQueryReference<unknown>): void; + // (undocumented) getQueryRef<TData = any>(cacheKey: CacheKey, createObservable: () => ObservableQuery<TData>): InternalQueryReference<TData>; } diff --git a/.api-reports/api-report.md b/.api-reports/api-report.md index 5bd4088b017..9b72b6ae88b 100644 --- a/.api-reports/api-report.md +++ b/.api-reports/api-report.md @@ -1332,17 +1332,11 @@ class InternalQueryReference<TData = unknown> { // (undocumented) softRetain(): () => void; // (undocumented) - strictModeFixAddToSuspenseCache(): void; - // (undocumented) get watchQueryOptions(): WatchQueryOptions<OperationVariables, TData>; } // @public (undocumented) interface InternalQueryReferenceOptions { - // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts - // - // (undocumented) - addToSuspenseCache?: (queryRef: InternalQueryReference<any>) => void; // (undocumented) autoDisposeTimeoutMs?: number; // (undocumented) @@ -2333,6 +2327,8 @@ export { QueryOptions } export interface QueryReference<TData = unknown, TVariables = unknown> { // @internal (undocumented) [PROMISE_SYMBOL]: QueryRefPromise<TData>; + // Warning: (ae-forgotten-export) The symbol "InternalQueryReference" needs to be exported by the entry point index.d.ts + // // @internal (undocumented) readonly [QUERY_REFERENCE_SYMBOL]: InternalQueryReference<TData>; toPromise(): Promise<QueryReference<TData, TVariables>>; From d1e61d64645079ec40c6d97504d23c1fabfdc563 Mon Sep 17 00:00:00 2001 From: Jerel Miller <jerelmiller@gmail.com> Date: Thu, 2 May 2024 15:35:00 -0600 Subject: [PATCH 09/10] Add additional changeset for revert of behavior --- .changeset/seven-forks-own.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/seven-forks-own.md diff --git a/.changeset/seven-forks-own.md b/.changeset/seven-forks-own.md new file mode 100644 index 00000000000..a8c779efa57 --- /dev/null +++ b/.changeset/seven-forks-own.md @@ -0,0 +1,6 @@ +--- +"@apollo/client": patch +--- + +Revert the change introduced in +[3.9.10](https://github.com/apollographql/apollo-client/releases/tag/v3.9.10) via #11738 that disposed of queryRefs synchronously. This change caused too many issues with strict mode. From 7d5ff9f08f61645008e4014d2b81d4388c1f0a95 Mon Sep 17 00:00:00 2001 From: jerelmiller <jerelmiller@users.noreply.github.com> Date: Fri, 3 May 2024 15:40:23 +0000 Subject: [PATCH 10/10] Clean up Prettier, Size-limit, and Api-Extractor --- .size-limits.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.size-limits.json b/.size-limits.json index 45dcdd441b8..855bf336313 100644 --- a/.size-limits.json +++ b/.size-limits.json @@ -1,4 +1,4 @@ { - "dist/apollo-client.min.cjs": 39614, + "dist/apollo-client.min.cjs": 39540, "import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32826 }