Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
at main 7.5 kB view raw
1'use client'; 2 3import type { 4 AnyVariables, 5 CombinedError, 6 GraphQLRequestParams, 7 Operation, 8 OperationContext, 9 RequestPolicy, 10} from 'urql'; 11import { createRequest, useQuery as orig_useQuery } from 'urql'; 12import { useUrqlValue } from './useUrqlValue'; 13 14/** Input arguments for the {@link useQuery} hook. 15 * 16 * @param query - The GraphQL query that `useQuery` executes. 17 * @param variables - The variables for the GraphQL query that `useQuery` executes. 18 */ 19export type UseQueryArgs< 20 Variables extends AnyVariables = AnyVariables, 21 Data = any, 22> = { 23 /** Updates the {@link RequestPolicy} for the executed GraphQL query operation. 24 * 25 * @remarks 26 * `requestPolicy` modifies the {@link RequestPolicy} of the GraphQL query operation 27 * that `useQuery` executes, and indicates a caching strategy for cache exchanges. 28 * 29 * For example, when set to `'cache-and-network'`, {@link useQuery} will 30 * receive a cached result with `stale: true` and an API request will be 31 * sent in the background. 32 * 33 * @see {@link OperationContext.requestPolicy} for where this value is set. 34 */ 35 requestPolicy?: RequestPolicy; 36 /** Updates the {@link OperationContext} for the executed GraphQL query operation. 37 * 38 * @remarks 39 * `context` may be passed to {@link useQuery}, to update the {@link OperationContext} 40 * of a query operation. This may be used to update the `context` that exchanges 41 * will receive for a single hook. 42 * 43 * Hint: This should be wrapped in a `useMemo` hook, to make sure that your 44 * component doesn’t infinitely update. 45 * 46 * @example 47 * ```ts 48 * const [result, reexecute] = useQuery({ 49 * query, 50 * context: useMemo(() => ({ 51 * additionalTypenames: ['Item'], 52 * }), []) 53 * }); 54 * ``` 55 */ 56 context?: Partial<OperationContext>; 57 /** Prevents {@link useQuery} from automatically executing GraphQL query operations. 58 * 59 * @remarks 60 * `pause` may be set to `true` to stop {@link useQuery} from executing 61 * automatically. The hook will stop receiving updates from the {@link Client} 62 * and won’t execute the query operation, until either it’s set to `false` 63 * or the {@link UseQueryExecute} function is called. 64 * 65 * @see {@link https://urql.dev/goto/docs/basics/react-preact/#pausing-usequery} for 66 * documentation on the `pause` option. 67 */ 68 pause?: boolean; 69} & GraphQLRequestParams<Data, Variables>; 70 71/** State of the current query, your {@link useQuery} hook is executing. 72 * 73 * @remarks 74 * `UseQueryState` is returned (in a tuple) by {@link useQuery} and 75 * gives you the updating {@link OperationResult} of GraphQL queries. 76 * 77 * Even when the query and variables passed to {@link useQuery} change, 78 * this state preserves the prior state and sets the `fetching` flag to 79 * `true`. 80 * This allows you to display the previous state, while implementing 81 * a separate loading indicator separately. 82 */ 83export interface UseQueryState< 84 Data = any, 85 Variables extends AnyVariables = AnyVariables, 86> { 87 /** Indicates whether `useQuery` is waiting for a new result. 88 * 89 * @remarks 90 * When `useQuery` is passed a new query and/or variables, it will 91 * start executing the new query operation and `fetching` is set to 92 * `true` until a result arrives. 93 * 94 * Hint: This is subtly different than whether the query is actually 95 * fetching, and doesn’t indicate whether a query is being re-executed 96 * in the background. For this, see {@link UseQueryState.stale}. 97 */ 98 fetching: boolean; 99 /** Indicates that the state is not fresh and a new result will follow. 100 * 101 * @remarks 102 * The `stale` flag is set to `true` when a new result for the query 103 * is expected and `useQuery` is waiting for it. This may indicate that 104 * a new request is being requested in the background. 105 * 106 * @see {@link OperationResult.stale} for the source of this value. 107 */ 108 stale: boolean; 109 /** The {@link OperationResult.data} for the executed query. */ 110 data?: Data; 111 /** The {@link OperationResult.error} for the executed query. */ 112 error?: CombinedError; 113 /** The {@link OperationResult.hasNext} for the executed query. */ 114 hasNext: boolean; 115 /** The {@link OperationResult.extensions} for the executed query. */ 116 extensions?: Record<string, any>; 117 /** The {@link Operation} that the current state is for. 118 * 119 * @remarks 120 * This is the {@link Operation} that is currently being executed. 121 * When {@link UseQueryState.fetching} is `true`, this is the 122 * last `Operation` that the current state was for. 123 */ 124 operation?: Operation<Data, Variables>; 125} 126 127/** Triggers {@link useQuery} to execute a new GraphQL query operation. 128 * 129 * @param opts - optionally, context options that will be merged with the hook's 130 * {@link UseQueryArgs.context} options and the `Client`’s options. 131 * 132 * @remarks 133 * When called, {@link useQuery} will re-execute the GraphQL query operation 134 * it currently holds, even if {@link UseQueryArgs.pause} is set to `true`. 135 * 136 * This is useful for executing a paused query or re-executing a query 137 * and get a new network result, by passing a new request policy. 138 * 139 * ```ts 140 * const [result, reexecuteQuery] = useQuery({ query }); 141 * 142 * const refresh = () => { 143 * // Re-execute the query with a network-only policy, skipping the cache 144 * reexecuteQuery({ requestPolicy: 'network-only' }); 145 * }; 146 * ``` 147 */ 148export type UseQueryExecute = (opts?: Partial<OperationContext>) => void; 149 150/** Result tuple returned by the {@link useQuery} hook. 151 * 152 * @remarks 153 * Similarly to a `useState` hook’s return value, 154 * the first element is the {@link useQuery}’s result and state, 155 * a {@link UseQueryState} object, 156 * and the second is used to imperatively re-execute the query 157 * via a {@link UseQueryExecute} function. 158 */ 159export type UseQueryResponse< 160 Data = any, 161 Variables extends AnyVariables = AnyVariables, 162> = [UseQueryState<Data, Variables>, UseQueryExecute]; 163 164/** Hook to run a GraphQL query and get updated GraphQL results. 165 * 166 * @param args - a {@link UseQueryArgs} object, to pass a `query`, `variables`, and options. 167 * @returns a {@link UseQueryResponse} tuple of a {@link UseQueryState} result, and re-execute function. 168 * 169 * @remarks 170 * `useQuery` allows GraphQL queries to be defined and executed. 171 * Given {@link UseQueryArgs.query}, it executes the GraphQL query with the 172 * context’s {@link Client}. 173 * 174 * The returned result updates when the `Client` has new results 175 * for the query, and changes when your input `args` change. 176 * 177 * Additionally, if the `suspense` option is enabled on the `Client`, 178 * the `useQuery` hook will suspend instead of indicating that it’s 179 * waiting for a result via {@link UseQueryState.fetching}. 180 * 181 * @see {@link https://urql.dev/goto/urql/docs/basics/react-preact/#queries} for `useQuery` docs. 182 * 183 * @example 184 * ```ts 185 * import { gql, useQuery } from 'urql'; 186 * 187 * const TodosQuery = gql` 188 * query { todos { id, title } } 189 * `; 190 * 191 * const Todos = () => { 192 * const [result, reexecuteQuery] = useQuery({ 193 * query: TodosQuery, 194 * variables: {}, 195 * }); 196 * // ... 197 * }; 198 * ``` 199 */ 200export function useQuery< 201 Data = any, 202 Variables extends AnyVariables = AnyVariables, 203>( 204 args: UseQueryArgs<Variables, Data> 205): UseQueryResponse<Data, Variables | undefined> { 206 const request = createRequest( 207 args.query, 208 (args.variables || {}) as AnyVariables 209 ); 210 useUrqlValue(request.key); 211 212 const [result, execute] = orig_useQuery(args); 213 214 useUrqlValue(request.key); 215 216 return [result, execute]; 217}