Skip to content

Commit

Permalink
Make useLazyQuery execution function return a promise
Browse files Browse the repository at this point in the history
Implements #7714
  • Loading branch information
brainkim committed Sep 30, 2021
1 parent 27f2f80 commit e8ee939
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 7 deletions.
38 changes: 38 additions & 0 deletions src/react/hooks/__tests__/useLazyQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -506,4 +506,42 @@ describe('useLazyQuery Hook', () => {
expect(result.current[1].loading).toBe(false);
expect(result.current[1].data).toEqual({ hello: 'from link' });
});

it('should return a promise from the execution function which resolves with the result', async () => {
const query = gql`{ hello }`;
const mocks = [
{
request: { query },
result: { data: { hello: 'world' } },
delay: 20,
},
];
const { result, waitForNextUpdate } = renderHook(
() => useLazyQuery(query),
{
wrapper: ({ children }) => (
<MockedProvider mocks={mocks}>
{children}
</MockedProvider>
),
},
);

expect(result.current[1].loading).toBe(false);
expect(result.current[1].data).toBe(undefined);
const execute = result.current[0];
const mock = jest.fn();
setTimeout(() => mock(execute()));

await waitForNextUpdate();
expect(result.current[1].loading).toBe(true);

await waitForNextUpdate();
expect(result.current[1].loading).toBe(false);
expect(result.current[1].data).toEqual({ hello: 'world' });

expect(mock).toHaveBeenCalledTimes(1);
expect(mock.mock.calls[0][0]).toBeInstanceOf(Promise);
expect(await mock.mock.calls[0][0]).toEqual(result.current[1]);
});
});
36 changes: 30 additions & 6 deletions src/react/hooks/useLazyQuery.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DocumentNode } from 'graphql';
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
import { useCallback, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';

import {
LazyQueryHookOptions,
Expand All @@ -26,31 +26,55 @@ export function useLazyQuery<TData = any, TVariables = OperationVariables>(
options?: LazyQueryHookOptions<TData, TVariables>
): QueryTuple<TData, TVariables> {
const [execution, setExecution] = useState<
{ called: boolean, options?: QueryLazyOptions<TVariables> }
{
called: boolean,
options?: QueryLazyOptions<TVariables>,
resolves: Array<(result: LazyQueryResult<TData, TVariables>) => void>,
}
>({
called: false,
resolves: [],
});

const execute = useCallback<
QueryTuple<TData, TVariables>[0]
>((executeOptions?: QueryLazyOptions<TVariables>) => {
let resolve!: (result: LazyQueryResult<TData, TVariables>) => void;
const promise = new Promise<LazyQueryResult<TData, TVariables>>(
(resolve1) => (resolve = resolve1),
);
setExecution((execution) => {
if (execution.called) {
result && result.refetch(executeOptions?.variables);
}

return { called: true, options: executeOptions };
return {
called: true,
resolves: [...execution.resolves, resolve],
options: executeOptions,
};
});

return promise;
}, []);

let result = useQuery<TData, TVariables>(query, {
const queryOptions = {
...options,
...execution.options,
// We don’t set skip to execution.called, because we need useQuery to call
// addQueryPromise, so that ssr calls waits for execute to be called.
fetchPolicy: execution.called ? options?.fetchPolicy : 'standby',
skip: undefined,
});
};

let result = useQuery<TData, TVariables>(query, queryOptions);
useEffect(() => {
const { resolves } = execution;
if (!result.loading && resolves.length) {
setExecution((execution) => ({ ...execution, resolves: [] }));
resolves.forEach((resolve) => resolve(result));
}
}, [result, execution]);

if (!execution.called) {
result = {
Expand All @@ -66,7 +90,7 @@ export function useLazyQuery<TData = any, TVariables = OperationVariables>(
for (const key of EAGER_METHODS) {
const method = result[key];
result[key] = (...args: any) => {
setExecution({ called: true });
setExecution((execution) => ({ ...execution, called: true }));
return (method as any)(...args);
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/react/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export interface QueryLazyOptions<TVariables> {
export type LazyQueryResult<TData, TVariables> = QueryResult<TData, TVariables>;

export type QueryTuple<TData, TVariables> = [
(options?: QueryLazyOptions<TVariables>) => void,
(options?: QueryLazyOptions<TVariables>) => Promise<LazyQueryResult<TData, TVariables>>,
LazyQueryResult<TData, TVariables>
];

Expand Down

0 comments on commit e8ee939

Please sign in to comment.