Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
alessbell committed Apr 18, 2023
1 parent ffb3538 commit c37dfd7
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 21 deletions.
69 changes: 57 additions & 12 deletions src/react/hooks/__tests__/useBackgroundQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import { ApolloProvider } from '../../context';
import { SuspenseCache } from '../../cache';
import { InMemoryCache } from '../../../cache';

function wait(delay: number) {
return new Promise((resolve) => setTimeout(resolve, delay));
}
// function wait(delay: number) {
// return new Promise((resolve) => setTimeout(resolve, delay));
// }

function renderIntegrationTest<Result>({
function renderIntegrationTest({
client,
variables,
}: {
Expand Down Expand Up @@ -125,7 +125,7 @@ function renderIntegrationTest<Result>({
return { ...rest, query, client: _client, renders };
}

function renderVariablesIntegrationTest<Result>({
function renderVariablesIntegrationTest({
variables,
client,
}: {
Expand Down Expand Up @@ -195,27 +195,39 @@ function renderVariablesIntegrationTest<Result>({
}: {
promise: Promise<ApolloQueryResult<QueryData>>;
}) {
const { data } = useReadQuery<QueryData>(promise);
const result = useReadQuery<QueryData>(promise);
return (
<div>
{data.character.id} - {data.character.name}
{result?.data?.character.id} - {result?.data?.character.name}
</div>
);
}

function ParentWithVariables({ variables }: { variables: QueryVariables }) {
function ParentWithVariables({
variables,
promise: _promise,
}: {
variables: QueryVariables;
promise?: Promise<ApolloQueryResult<QueryData>>;
}) {
const { promise } = useBackgroundQuery(query, { variables });
// count renders in the parent component
renders.count++;
return <Child promise={promise} />;
return <Child promise={_promise || promise} />;
}

function App({ variables }: { variables: QueryVariables }) {
function App({
variables,
promise: _promise,
}: {
variables: QueryVariables;
promise?: Promise<ApolloQueryResult<QueryData>>;
}) {
return (
<ApolloProvider client={_client} suspenseCache={suspenseCache}>
<ErrorBoundary {...errorBoundaryProps}>
<Suspense fallback={<SuspenseFallback />}>
<ParentWithVariables variables={variables} />
<ParentWithVariables variables={variables} promise={_promise} />
</Suspense>
</ErrorBoundary>
</ApolloProvider>
Expand Down Expand Up @@ -540,7 +552,6 @@ describe('useBackgroundQuery', () => {
});

it('reacts to variables updates', async () => {
// TODO: this test triggers the warning re: promise not stable across renders
const { App, renders, rerender } = renderVariablesIntegrationTest({
variables: { id: '1' },
});
Expand All @@ -558,6 +569,40 @@ describe('useBackgroundQuery', () => {
expect(await screen.findByText('2 - Black Widow')).toBeInTheDocument();
});

it.only('logs warning if subscription cannot be found', async () => {
// TODO: this test triggers the warning re: promise not stable across renders
const { App, renders, rerender, query } = renderVariablesIntegrationTest({
variables: { id: '1' },
});

expect(renders.suspenseCount).toBe(1);
expect(screen.getByText('loading')).toBeInTheDocument();

expect(await screen.findByText('1 - Spider-Man')).toBeInTheDocument();

// render useBackgroundQuery with new suspense cache, but same variables + query
// const { promise } = useBackgroundQuery(query, { variables });
const suspenseCache = new SuspenseCache();
const { result } = renderHook(() => useBackgroundQuery(query), {
wrapper: ({ children }) => (
<ApolloProvider
suspenseCache={suspenseCache}
client={new ApolloClient({ cache: new InMemoryCache() })}
>
{children}
</ApolloProvider>
),
});
console.log(result.current.promise);
// pass promise here
rerender(<App variables={{ id: '2' }} promise={result.current.promise} />);

expect(renders.suspenseCount).toBe(2);
expect(screen.getByText('loading')).toBeInTheDocument();

expect(await screen.findByText('2 - Black Widow')).toBeInTheDocument();
});

// todo: do the same on refetch
// it('suspends when partial data is in the cache (test all cache policies)', async () => {

Expand Down
27 changes: 18 additions & 9 deletions src/react/hooks/useBackgroundQuery.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useRef, useCallback } from 'react';
import React, { useMemo, useRef, useCallback } from 'react';
import {
ApolloClient,
DocumentNode,
Expand Down Expand Up @@ -166,18 +166,27 @@ export function useReadQuery<TData>(
suspenseCache?: SuspenseCache
) {
const _suspenseCache = suspenseCache || useSuspenseCache();
const prevPromise = useRef(promise);

if (prevPromise.current !== promise) {
invariant.warn(
'The promise you have provided is not stable across renders; this may cause `useReadQuery` to return incorrect data. If you are passing it via `options`, please ensure you are providing the same SuspenseCache to both `useBackgroundQuery` and `useReadQuery`.'
);
}

prevPromise.current = promise;
// const prevPromise = useRef(promise);
// React.useEffect(() => {
// console.log('effect running');
// if (promise !== prevPromise.current) {
// invariant.warn(
// 'The promise you have provided is not stable across renders. This may cause useReadQuery to return incorrect data.'
// );
// }
// }, [promise, prevPromise.current]);
// prevPromise.current = promise;

const result = __use(promise);
const subscription = _suspenseCache.getSubscriptionFromPromise(promise);

if (!subscription) {
invariant.warn(
'useReadQuery failed to find the subscription to your request from the promise provided. Please ensure you are passing promise returned by useBackgroundQuery. If you are passing the SuspenseCache directly to the hook via options, ensure the same SuspenseCache is being used by both useBackgroundQuery and useReadQuery.'
);
}
console.log({ result });
return useSyncExternalStore(
subscription?.listen ||
(() => () => {
Expand Down

0 comments on commit c37dfd7

Please sign in to comment.