Skip to content

Commit

Permalink
Use useNormalizedOptions for useLazyQuery as well as useQuery.
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamn committed Mar 3, 2022
1 parent 75119fe commit a0ce008
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 37 deletions.
40 changes: 40 additions & 0 deletions src/react/hooks/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useRef } from "react";
import equal from "@wry/equality";

import {
QueryHookOptions,
LazyQueryHookOptions,
} from "../types/types";

// I would have made this function a method of the InternalState class, but it
// needs to run before we get the client from useApolloClient in the useQuery
// function above, just in case the options function returns options.client as
// an override for the ApolloClient instance provided by React context.
export function useNormalizedOptions<
TOptions extends
| QueryHookOptions<any, any>
| LazyQueryHookOptions<any, any>
>(
optionsOrFunction?:
| TOptions
| ((prevOptions: TOptions) => TOptions)
): TOptions {
const optionsRef = useRef<TOptions>();
let options: TOptions = optionsRef.current || Object.create(null);

if (typeof optionsOrFunction === "function") {
const newOptions = optionsOrFunction(options);
if (newOptions !== options) {
Object.assign(options, newOptions, newOptions.variables && {
variables: {
...options.variables,
...newOptions.variables,
},
});
}
} else if (optionsOrFunction && !equal(optionsOrFunction, options)) {
options = optionsOrFunction;
}

return optionsRef.current = options;
}
10 changes: 8 additions & 2 deletions src/react/hooks/useLazyQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import { DocumentNode } from 'graphql';
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
import { useCallback, useMemo, useState } from 'react';

import { OperationVariables } from '../../core';
import {
LazyQueryHookOptions,
QueryLazyOptions,
QueryTuple,
LazyQueryHookOptionsFunction,
} from '../types/types';
import { useQuery } from './useQuery';
import { OperationVariables } from '../../core';
import { useNormalizedOptions } from './options';

// The following methods, when called will execute the query, regardless of
// whether the useLazyQuery execute function was called before.
Expand All @@ -22,8 +24,12 @@ const EAGER_METHODS = [

export function useLazyQuery<TData = any, TVariables = OperationVariables>(
query: DocumentNode | TypedDocumentNode<TData, TVariables>,
options?: LazyQueryHookOptions<TData, TVariables>
optionsOrFunction?:
| LazyQueryHookOptions<TData, TVariables>
| LazyQueryHookOptionsFunction<TData, TVariables>
): QueryTuple<TData, TVariables> {
const options = useNormalizedOptions(optionsOrFunction);

const [execution, setExecution] = useState<{
called: boolean,
options?: QueryLazyOptions<TVariables>,
Expand Down
37 changes: 2 additions & 35 deletions src/react/hooks/useQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,20 @@ import {
QueryHookOptions,
QueryResult,
ObservableQueryFields,
QueryHookOptionsFunction,
} from '../types/types';

import { DocumentType, verifyDocumentType } from '../parser';
import { useApolloClient } from './useApolloClient';
import { canUseWeakMap, isNonEmptyArray } from '../../utilities';
import { useNormalizedOptions } from './options';

const {
prototype: {
hasOwnProperty,
},
} = Object;

type QueryHookOptionsFunction<TData, TVariables> = (
options: QueryHookOptions<TData, TVariables>,
) => QueryHookOptions<TData, TVariables>;

export function useQuery<
TData = any,
TVariables = OperationVariables,
Expand All @@ -50,37 +48,6 @@ export function useQuery<
).useQuery(options);
}

// I would have made this function a method of the InternalState class, but it
// needs to run before we get the client from useApolloClient in the useQuery
// function above, just in case the options function returns options.client as
// an override for the ApolloClient instance provided by React context.
function useNormalizedOptions<TData, TVariables>(
optionsOrFunction:
| QueryHookOptions<TData, TVariables>
| QueryHookOptionsFunction<TData, TVariables>
| undefined,
): QueryHookOptions<TData, TVariables> {
const optionsRef = useRef<QueryHookOptions<TData, TVariables>>();
let options: QueryHookOptions<TData, TVariables> =
optionsRef.current || Object.create(null);

if (typeof optionsOrFunction === "function") {
const newOptions = optionsOrFunction(options);
if (newOptions !== options) {
Object.assign(options, newOptions, newOptions.variables && {
variables: {
...options.variables,
...newOptions.variables,
},
});
}
} else if (optionsOrFunction && !equal(optionsOrFunction, options)) {
options = optionsOrFunction;
}

return optionsRef.current = options;
}

function useInternalState<TData, TVariables>(
client: ApolloClient<any>,
query: DocumentNode | TypedDocumentNode<TData, TVariables>,
Expand Down
8 changes: 8 additions & 0 deletions src/react/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,21 @@ export interface QueryHookOptions<TData = any, TVariables = OperationVariables>
query?: DocumentNode | TypedDocumentNode<TData, TVariables>;
}

export type QueryHookOptionsFunction<TData, TVariables> = (
prevOptions: QueryHookOptions<TData, TVariables>,
) => QueryHookOptions<TData, TVariables>;

export interface LazyQueryHookOptions<
TData = any,
TVariables = OperationVariables
> extends Omit<QueryFunctionOptions<TData, TVariables>, 'skip'> {
query?: DocumentNode | TypedDocumentNode<TData, TVariables>;
}

export type LazyQueryHookOptionsFunction<TData, TVariables> = (
prevOptions: LazyQueryHookOptions<TData, TVariables>,
) => LazyQueryHookOptions<TData, TVariables>;

export interface QueryLazyOptions<TVariables> {
variables?: TVariables;
context?: DefaultContext;
Expand Down

0 comments on commit a0ce008

Please sign in to comment.