diff --git a/src/react/cache/QueryReference.ts b/src/react/cache/QueryReference.ts index 5b2fc14c643..b15879e4a29 100644 --- a/src/react/cache/QueryReference.ts +++ b/src/react/cache/QueryReference.ts @@ -22,6 +22,7 @@ type FetchMoreOptions = Parameters< >[0]; const QUERY_REFERENCE_SYMBOL: unique symbol = Symbol(); +const PROMISE_SYMBOL: unique symbol = Symbol(); /** * A `QueryReference` is an opaque object returned by {@link useBackgroundQuery}. * A child component reading the `QueryReference` via {@link useReadQuery} will @@ -29,6 +30,7 @@ const QUERY_REFERENCE_SYMBOL: unique symbol = Symbol(); */ export interface QueryReference { [QUERY_REFERENCE_SYMBOL]: InternalQueryReference; + [PROMISE_SYMBOL]: Promise>; } interface InternalQueryReferenceOptions { @@ -38,15 +40,26 @@ interface InternalQueryReferenceOptions { } export function wrapQueryRef( - internalQueryRef: InternalQueryReference + internalQueryRef: InternalQueryReference, + promise: Promise> ): QueryReference { - return { [QUERY_REFERENCE_SYMBOL]: internalQueryRef }; + return { + [QUERY_REFERENCE_SYMBOL]: internalQueryRef, + [PROMISE_SYMBOL]: promise, + }; } export function unwrapQueryRef( queryRef: QueryReference -): InternalQueryReference { - return queryRef[QUERY_REFERENCE_SYMBOL]; +): [InternalQueryReference, () => Promise>] { + return [queryRef[QUERY_REFERENCE_SYMBOL], () => queryRef[PROMISE_SYMBOL]]; +} + +export function updateWrappedQueryRef( + queryRef: QueryReference, + promise: Promise> +) { + queryRef[PROMISE_SYMBOL] = promise; } const OBSERVED_CHANGED_OPTIONS = [ @@ -69,6 +82,7 @@ export class InternalQueryReference { public readonly observable: ObservableQuery; public promiseCache?: Map>>; + public lastObservedPromise?: Promise>; public promise: Promise>; private subscription: ObservableSubscription; diff --git a/src/react/hooks/__tests__/useBackgroundQuery.test.tsx b/src/react/hooks/__tests__/useBackgroundQuery.test.tsx index 08121af7f27..ea77002780c 100644 --- a/src/react/hooks/__tests__/useBackgroundQuery.test.tsx +++ b/src/react/hooks/__tests__/useBackgroundQuery.test.tsx @@ -641,7 +641,7 @@ describe("useBackgroundQuery", () => { const [queryRef] = result.current; - const _result = await unwrapQueryRef(queryRef).promise; + const _result = await unwrapQueryRef(queryRef)[0].promise; expect(_result).toEqual({ data: { hello: "world 1" }, @@ -678,7 +678,7 @@ describe("useBackgroundQuery", () => { const [queryRef] = result.current; - const _result = await unwrapQueryRef(queryRef).promise; + const _result = await unwrapQueryRef(queryRef)[0].promise; await waitFor(() => { expect(_result).toEqual({ @@ -719,7 +719,7 @@ describe("useBackgroundQuery", () => { const [queryRef] = result.current; - const _result = await unwrapQueryRef(queryRef).promise; + const _result = await unwrapQueryRef(queryRef)[0].promise; await waitFor(() => { expect(_result).toMatchObject({ @@ -779,7 +779,7 @@ describe("useBackgroundQuery", () => { const [queryRef] = result.current; - const _result = await unwrapQueryRef(queryRef).promise; + const _result = await unwrapQueryRef(queryRef)[0].promise; const resultSet = new Set(_result.data.results); const values = Array.from(resultSet).map((item) => item.value); @@ -840,7 +840,7 @@ describe("useBackgroundQuery", () => { const [queryRef] = result.current; - const _result = await unwrapQueryRef(queryRef).promise; + const _result = await unwrapQueryRef(queryRef)[0].promise; const resultSet = new Set(_result.data.results); const values = Array.from(resultSet).map((item) => item.value); @@ -882,7 +882,7 @@ describe("useBackgroundQuery", () => { const [queryRef] = result.current; - const _result = await unwrapQueryRef(queryRef).promise; + const _result = await unwrapQueryRef(queryRef)[0].promise; expect(_result).toEqual({ data: { hello: "from link" }, @@ -922,7 +922,7 @@ describe("useBackgroundQuery", () => { const [queryRef] = result.current; - const _result = await unwrapQueryRef(queryRef).promise; + const _result = await unwrapQueryRef(queryRef)[0].promise; expect(_result).toEqual({ data: { hello: "from cache" }, @@ -969,7 +969,7 @@ describe("useBackgroundQuery", () => { const [queryRef] = result.current; - const _result = await unwrapQueryRef(queryRef).promise; + const _result = await unwrapQueryRef(queryRef)[0].promise; expect(_result).toEqual({ data: { foo: "bar", hello: "from link" }, @@ -1009,7 +1009,7 @@ describe("useBackgroundQuery", () => { const [queryRef] = result.current; - const _result = await unwrapQueryRef(queryRef).promise; + const _result = await unwrapQueryRef(queryRef)[0].promise; expect(_result).toEqual({ data: { hello: "from link" }, @@ -1052,7 +1052,7 @@ describe("useBackgroundQuery", () => { const [queryRef] = result.current; - const _result = await unwrapQueryRef(queryRef).promise; + const _result = await unwrapQueryRef(queryRef)[0].promise; expect(_result).toEqual({ data: { hello: "from link" }, diff --git a/src/react/hooks/useBackgroundQuery.ts b/src/react/hooks/useBackgroundQuery.ts index 20b2e978aa7..8659ed18c86 100644 --- a/src/react/hooks/useBackgroundQuery.ts +++ b/src/react/hooks/useBackgroundQuery.ts @@ -7,7 +7,11 @@ import type { WatchQueryOptions, } from "../../core/index.js"; import { useApolloClient } from "./useApolloClient.js"; -import { wrapQueryRef } from "../cache/QueryReference.js"; +import { + unwrapQueryRef, + updateWrappedQueryRef, + wrapQueryRef, +} from "../cache/QueryReference.js"; import type { QueryReference } from "../cache/QueryReference.js"; import type { BackgroundQueryHookOptions, NoInfer } from "../types/types.js"; import { __use } from "./internal/index.js"; @@ -202,13 +206,16 @@ export function useBackgroundQuery< client.watchQuery(watchQueryOptions as WatchQueryOptions) ); - const [promiseCache, setPromiseCache] = React.useState( - () => new Map([[queryRef.key, queryRef.promise]]) - ); - + const [wrapped, setWrappedQueryRef] = React.useState({ + current: wrapQueryRef(queryRef, queryRef.promise), + }); + if (unwrapQueryRef(wrapped.current)[0] !== queryRef) { + wrapped.current = wrapQueryRef(queryRef, queryRef.promise); + } + let wrappedQueryRef = wrapped.current; if (queryRef.didChangeOptions(watchQueryOptions)) { const promise = queryRef.applyOptions(watchQueryOptions); - promiseCache.set(queryRef.key, promise); + updateWrappedQueryRef(wrappedQueryRef, promise); } React.useEffect(() => queryRef.retain(), [queryRef]); @@ -217,9 +224,7 @@ export function useBackgroundQuery< (options) => { const promise = queryRef.fetchMore(options as FetchMoreQueryOptions); - setPromiseCache((promiseCache) => - new Map(promiseCache).set(queryRef.key, queryRef.promise) - ); + setWrappedQueryRef({ current: wrapQueryRef(queryRef, queryRef.promise) }); return promise; }, @@ -230,22 +235,13 @@ export function useBackgroundQuery< (variables) => { const promise = queryRef.refetch(variables); - setPromiseCache((promiseCache) => - new Map(promiseCache).set(queryRef.key, queryRef.promise) - ); + setWrappedQueryRef({ current: wrapQueryRef(queryRef, queryRef.promise) }); return promise; }, [queryRef] ); - queryRef.promiseCache = promiseCache; - - const wrappedQueryRef = React.useMemo( - () => wrapQueryRef(queryRef), - [queryRef] - ); - return [ didFetchResult.current ? wrappedQueryRef : void 0, { fetchMore, refetch }, diff --git a/src/react/hooks/useReadQuery.ts b/src/react/hooks/useReadQuery.ts index 803535c4878..41e1efe47e5 100644 --- a/src/react/hooks/useReadQuery.ts +++ b/src/react/hooks/useReadQuery.ts @@ -1,9 +1,11 @@ import * as React from "rehackt"; -import { unwrapQueryRef } from "../cache/QueryReference.js"; +import { + unwrapQueryRef, + updateWrappedQueryRef, +} from "../cache/QueryReference.js"; import type { QueryReference } from "../cache/QueryReference.js"; import { __use } from "./internal/index.js"; import { toApolloError } from "./useSuspenseQuery.js"; -import { invariant } from "../../utilities/globals/index.js"; import { useSyncExternalStore } from "./useSyncExternalStore.js"; import type { ApolloError } from "../../errors/index.js"; import type { NetworkStatus } from "../../core/index.js"; @@ -36,32 +38,22 @@ export interface UseReadQueryResult { export function useReadQuery( queryRef: QueryReference ): UseReadQueryResult { - const internalQueryRef = unwrapQueryRef(queryRef); - invariant( - internalQueryRef.promiseCache, - "It appears that `useReadQuery` was used outside of `useBackgroundQuery`. " + - "`useReadQuery` is only supported for use with `useBackgroundQuery`. " + - "Please ensure you are passing the `queryRef` returned from `useBackgroundQuery`." - ); - - const { promiseCache, key } = internalQueryRef; + const [internalQueryRef, getPromise] = unwrapQueryRef(queryRef); - if (!promiseCache.has(key)) { - promiseCache.set(key, internalQueryRef.promise); - } + console.log(internalQueryRef); const promise = useSyncExternalStore( React.useCallback( (forceUpdate) => { return internalQueryRef.listen((promise) => { - internalQueryRef.promiseCache!.set(internalQueryRef.key, promise); + updateWrappedQueryRef(queryRef, promise); forceUpdate(); }); }, [internalQueryRef] ), - () => promiseCache.get(key)!, - () => promiseCache.get(key)! + getPromise, + getPromise ); const result = __use(promise);