Skip to content

Commit

Permalink
WIP status: useBackgroundQuery
Browse files Browse the repository at this point in the history
  • Loading branch information
phryneas committed Nov 15, 2023
1 parent b8c51af commit 79577b5
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 50 deletions.
22 changes: 18 additions & 4 deletions src/react/cache/QueryReference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ type FetchMoreOptions<TData> = 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
* suspend until the promise resolves.
*/
export interface QueryReference<TData = unknown> {
[QUERY_REFERENCE_SYMBOL]: InternalQueryReference<TData>;
[PROMISE_SYMBOL]: Promise<ApolloQueryResult<TData>>;
}

interface InternalQueryReferenceOptions {
Expand All @@ -38,15 +40,26 @@ interface InternalQueryReferenceOptions {
}

export function wrapQueryRef<TData>(
internalQueryRef: InternalQueryReference<TData>
internalQueryRef: InternalQueryReference<TData>,
promise: Promise<ApolloQueryResult<TData>>
): QueryReference<TData> {
return { [QUERY_REFERENCE_SYMBOL]: internalQueryRef };
return {
[QUERY_REFERENCE_SYMBOL]: internalQueryRef,
[PROMISE_SYMBOL]: promise,
};
}

export function unwrapQueryRef<TData>(
queryRef: QueryReference<TData>
): InternalQueryReference<TData> {
return queryRef[QUERY_REFERENCE_SYMBOL];
): [InternalQueryReference<TData>, () => Promise<ApolloQueryResult<TData>>] {
return [queryRef[QUERY_REFERENCE_SYMBOL], () => queryRef[PROMISE_SYMBOL]];
}

export function updateWrappedQueryRef<TData>(
queryRef: QueryReference<TData>,
promise: Promise<ApolloQueryResult<TData>>
) {
queryRef[PROMISE_SYMBOL] = promise;
}

const OBSERVED_CHANGED_OPTIONS = [
Expand All @@ -69,6 +82,7 @@ export class InternalQueryReference<TData = unknown> {
public readonly observable: ObservableQuery<TData>;

public promiseCache?: Map<CacheKey, Promise<ApolloQueryResult<TData>>>;
public lastObservedPromise?: Promise<ApolloQueryResult<TData>>;
public promise: Promise<ApolloQueryResult<TData>>;

private subscription: ObservableSubscription;
Expand Down
20 changes: 10 additions & 10 deletions src/react/hooks/__tests__/useBackgroundQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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" },
Expand Down Expand Up @@ -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" },
Expand Down Expand Up @@ -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" },
Expand Down Expand Up @@ -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" },
Expand Down Expand Up @@ -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" },
Expand Down
34 changes: 15 additions & 19 deletions src/react/hooks/useBackgroundQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -202,13 +206,16 @@ export function useBackgroundQuery<
client.watchQuery(watchQueryOptions as WatchQueryOptions<any, any>)
);

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]);
Expand All @@ -217,9 +224,7 @@ export function useBackgroundQuery<
(options) => {
const promise = queryRef.fetchMore(options as FetchMoreQueryOptions<any>);

setPromiseCache((promiseCache) =>
new Map(promiseCache).set(queryRef.key, queryRef.promise)
);
setWrappedQueryRef({ current: wrapQueryRef(queryRef, queryRef.promise) });

return promise;
},
Expand All @@ -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 },
Expand Down
26 changes: 9 additions & 17 deletions src/react/hooks/useReadQuery.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -36,32 +38,22 @@ export interface UseReadQueryResult<TData = unknown> {
export function useReadQuery<TData>(
queryRef: QueryReference<TData>
): UseReadQueryResult<TData> {
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);
Expand Down

0 comments on commit 79577b5

Please sign in to comment.