Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
at main 6.6 kB view raw
1import type { AnyVariables, Client, DocumentInput } from '@urql/core'; 2import type { WatchStopHandle } from 'vue'; 3import { getCurrentInstance, onMounted, onBeforeUnmount } from 'vue'; 4 5import { useClient } from './useClient'; 6 7import type { UseQueryArgs, UseQueryResponse } from './useQuery'; 8import { callUseQuery } from './useQuery'; 9 10import type { UseMutationResponse } from './useMutation'; 11import { callUseMutation } from './useMutation'; 12 13import type { 14 UseSubscriptionArgs, 15 SubscriptionHandlerArg, 16 UseSubscriptionResponse, 17} from './useSubscription'; 18import { callUseSubscription } from './useSubscription'; 19 20/** Handle to create GraphQL operations outside of Vue’s `setup` functions. 21 * 22 * @remarks 23 * The `ClientHandle` object is created inside a Vue `setup` function but 24 * allows its methods to be called outside of `setup` functions, delaying 25 * the creation of GraphQL operations, as an alternative to pausing queries 26 * or subscriptions. 27 * 28 * This is also important when chaining multiple functions inside an 29 * `async setup()` function. 30 * 31 * Hint: If you only need a single, non-updating result and want to execute 32 * queries programmatically, it may be easier to call the {@link Client.query} 33 * method. 34 */ 35export interface ClientHandle { 36 /** The {@link Client} that’ll be used to execute GraphQL operations. */ 37 client: Client; 38 39 /** Calls {@link useQuery} outside of a synchronous Vue `setup` function. 40 * 41 * @param args - a {@link UseQueryArgs} object, to pass a `query`, `variables`, and options. 42 * @returns a {@link UseQueryResponse} object. 43 * 44 * @remarks 45 * Creates a {@link UseQueryResponse} outside of a synchronous Vue `setup` 46 * function or when chained in an `async setup()` function. 47 */ 48 useQuery<T = any, V extends AnyVariables = AnyVariables>( 49 args: UseQueryArgs<T, V> 50 ): UseQueryResponse<T, V>; 51 52 /** Calls {@link useSubscription} outside of a synchronous Vue `setup` function. 53 * 54 * @param args - a {@link UseSubscriptionArgs} object, to pass a `query`, `variables`, and options. 55 * @param handler - optionally, a {@link SubscriptionHandler} function to combine multiple subscription results. 56 * @returns a {@link UseSubscriptionResponse} object. 57 * 58 * @remarks 59 * Creates a {@link UseSubscriptionResponse} outside of a synchronous Vue `setup` 60 * function or when chained in an `async setup()` function. 61 */ 62 useSubscription<T = any, R = T, V extends AnyVariables = AnyVariables>( 63 args: UseSubscriptionArgs<T, V>, 64 handler?: SubscriptionHandlerArg<T, R> 65 ): UseSubscriptionResponse<T, R, V>; 66 67 /** Calls {@link useMutation} outside of a synchronous Vue `setup` function. 68 * 69 * @param query - a GraphQL mutation document which `useMutation` will execute. 70 * @returns a {@link UseMutationResponse} object. 71 * 72 * @remarks 73 * Creates a {@link UseMutationResponse} outside of a synchronous Vue `setup` 74 * function or when chained in an `async setup()` function. 75 */ 76 useMutation<T = any, V extends AnyVariables = AnyVariables>( 77 query: DocumentInput<T, V> 78 ): UseMutationResponse<T, V>; 79} 80 81/** Creates a {@link ClientHandle} inside a Vue `setup` function. 82 * 83 * @remarks 84 * `useClientHandle` creates and returns a {@link ClientHandle} 85 * when called in a Vue `setup` function, which allows queries, 86 * mutations, and subscriptions to be created _outside_ of 87 * `setup` functions. 88 * 89 * This is also important when chaining multiple functions inside an 90 * `async setup()` function. 91 * 92 * {@link useQuery} and other GraphQL functions must usually 93 * be created in Vue `setup` functions so they can stop GraphQL 94 * operations when your component unmounts. However, while they 95 * queries and subscriptions can be paused, sometimes it’s easier 96 * to delay the creation of their response objects. 97 * 98 * 99 * @example 100 * ```ts 101 * import { ref, computed } from 'vue'; 102 * import { gql, useClientHandle } from '@urql/vue'; 103 * 104 * export default { 105 * async setup() { 106 * const handle = useClientHandle(); 107 * 108 * const pokemons = await handle.useQuery({ 109 * query: gql`{ pokemons(limit: 10) { id, name } }`, 110 * }); 111 * 112 * const index = ref(0); 113 * 114 * // The `handle` allows another `useQuery` call to now be setup again 115 * const pokemon = await handle.useQuery({ 116 * query: gql` 117 * query ($id: ID!) { 118 * pokemon(id: $id) { id, name } 119 * } 120 * `, 121 * variables: computed(() => ({ 122 * id: pokemons.data.value.pokemons[index.value].id, 123 * }), 124 * }); 125 * } 126 * }; 127 * ``` 128 */ 129export function useClientHandle(): ClientHandle { 130 const client = useClient(); 131 const stops: WatchStopHandle[] = []; 132 133 onBeforeUnmount(() => { 134 let stop: WatchStopHandle | void; 135 while ((stop = stops.shift())) stop(); 136 }); 137 138 const handle: ClientHandle = { 139 client: client.value, 140 141 useQuery<T = any, V extends AnyVariables = AnyVariables>( 142 args: UseQueryArgs<T, V> 143 ): UseQueryResponse<T, V> { 144 return callUseQuery(args, client, stops); 145 }, 146 147 useSubscription<T = any, R = T, V extends AnyVariables = AnyVariables>( 148 args: UseSubscriptionArgs<T, V>, 149 handler?: SubscriptionHandlerArg<T, R> 150 ): UseSubscriptionResponse<T, R, V> { 151 return callUseSubscription(args, handler, client, stops); 152 }, 153 154 useMutation<T = any, V extends AnyVariables = AnyVariables>( 155 query: DocumentInput<T, V> 156 ): UseMutationResponse<T, V> { 157 return callUseMutation(query, client); 158 }, 159 }; 160 161 if (process.env.NODE_ENV !== 'production') { 162 onMounted(() => { 163 Object.assign(handle, { 164 useQuery<T = any, V extends AnyVariables = AnyVariables>( 165 args: UseQueryArgs<T, V> 166 ): UseQueryResponse<T, V> { 167 if (process.env.NODE_ENV !== 'production' && !getCurrentInstance()) { 168 throw new Error( 169 '`handle.useQuery()` should only be called in the `setup()` or a lifecycle hook.' 170 ); 171 } 172 173 return callUseQuery(args, client, stops); 174 }, 175 176 useSubscription<T = any, R = T, V extends AnyVariables = AnyVariables>( 177 args: UseSubscriptionArgs<T, V>, 178 handler?: SubscriptionHandlerArg<T, R> 179 ): UseSubscriptionResponse<T, R, V> { 180 if (process.env.NODE_ENV !== 'production' && !getCurrentInstance()) { 181 throw new Error( 182 '`handle.useSubscription()` should only be called in the `setup()` or a lifecycle hook.' 183 ); 184 } 185 186 return callUseSubscription(args, handler, client, stops); 187 }, 188 }); 189 }); 190 } 191 192 return handle; 193}