Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
at main 5.8 kB view raw
1/* eslint-disable react-hooks/rules-of-hooks */ 2 3import type { Ref } from 'vue'; 4import { shallowRef } from 'vue'; 5import { pipe, onPush, filter, toPromise, take } from 'wonka'; 6 7import type { 8 Client, 9 AnyVariables, 10 CombinedError, 11 Operation, 12 OperationContext, 13 OperationResult, 14 DocumentInput, 15} from '@urql/core'; 16 17import { useClient } from './useClient'; 18import { 19 createRequestWithArgs, 20 type MaybeRefOrGetter, 21 useRequestState, 22} from './utils'; 23 24/** State of the last mutation executed by {@link useMutation}. 25 * 26 * @remarks 27 * `UseMutationResponse` is returned by {@link useMutation} and 28 * gives you the {@link OperationResult} of the last executed mutation, 29 * and a {@link UseMutationResponse.executeMutation} method to 30 * start mutations. 31 * 32 * Even if the mutation document passed to {@link useMutation} changes, 33 * the state isn’t reset, so you can keep displaying the previous result. 34 */ 35export interface UseMutationResponse<T, V extends AnyVariables = AnyVariables> { 36 /** Indicates whether `useMutation` is currently executing a mutation. */ 37 fetching: Ref<boolean>; 38 /** Indicates that the mutation result is not fresh. 39 * 40 * @remarks 41 * The `stale` flag is set to `true` when a new result for the mutation 42 * is expected. 43 * This is mostly unused for mutations and will rarely affect you, and 44 * is more relevant for queries. 45 * 46 * @see {@link OperationResult.stale} for the source of this value. 47 */ 48 stale: Ref<boolean>; 49 /** Reactive {@link OperationResult.data} for the executed mutation. */ 50 data: Ref<T | undefined>; 51 /** Reactive {@link OperationResult.error} for the executed mutation. */ 52 error: Ref<CombinedError | undefined>; 53 /** Reactive {@link OperationResult.extensions} for the executed mutation. */ 54 extensions: Ref<Record<string, any> | undefined>; 55 /** Reactive {@link Operation} that the current state is for. 56 * 57 * @remarks 58 * This is the mutation {@link Operation} that has last been executed. 59 * When {@link UseQueryState.fetching} is `true`, this is the 60 * last `Operation` that the current state was for. 61 */ 62 operation: Ref<Operation<T, V> | undefined>; 63 /** The {@link OperationResult.hasNext} for the executed query. */ 64 hasNext: Ref<boolean>; 65 /** Triggers {@link useMutation} to execute its GraphQL mutation operation. 66 * 67 * @param variables - variables using which the mutation will be executed. 68 * @param context - optionally, context options that will be merged with 69 * the `Client`’s options. 70 * @returns the {@link OperationResult} of the mutation. 71 * 72 * @remarks 73 * When called, {@link useMutation} will start the GraphQL mutation 74 * it currently holds and use the `variables` passed to it. 75 * 76 * Once the mutation response comes back from the API, its 77 * returned promise will resolve to the mutation’s {@link OperationResult} 78 * and the {@link UseMutationResponse} will be updated with the result. 79 * 80 * @example 81 * ```ts 82 * const result = useMutation(UpdateTodo); 83 * const start = async ({ id, title }) => { 84 * const result = await result.executeMutation({ id, title }); 85 * }; 86 */ 87 executeMutation( 88 variables: V, 89 context?: Partial<OperationContext> 90 ): Promise<OperationResult<T>>; 91} 92 93/** Function to create a GraphQL mutation, run by passing variables to {@link UseMutationResponse.executeMutation} 94 * 95 * @param query - a GraphQL mutation document which `useMutation` will execute. 96 * @returns a {@link UseMutationResponse} object. 97 * 98 * @remarks 99 * `useMutation` allows GraphQL mutations to be defined inside Vue `setup` functions, 100 * and keeps its state after the mutation is started. Mutations can be started by calling 101 * {@link UseMutationResponse.executeMutation} with variables. 102 * 103 * The returned result updates when a mutation is executed and keeps 104 * track of the last mutation result. 105 * 106 * @see {@link https://urql.dev/goto/docs/basics/vue#mutations} for `useMutation` docs. 107 * 108 * @example 109 * ```ts 110 * import { gql, useMutation } from '@urql/vue'; 111 * 112 * const UpdateTodo = gql` 113 * mutation ($id: ID!, $title: String!) { 114 * updateTodo(id: $id, title: $title) { 115 * id, title 116 * } 117 * } 118 * `; 119 * 120 * export default { 121 * setup() { 122 * const result = useMutation(UpdateTodo); 123 * const start = async ({ id, title }) => { 124 * const result = await result.executeMutation({ id, title }); 125 * }; 126 * // ... 127 * }, 128 * }; 129 * ``` 130 */ 131export function useMutation<T = any, V extends AnyVariables = AnyVariables>( 132 query: DocumentInput<T, V> 133): UseMutationResponse<T, V> { 134 return callUseMutation(query); 135} 136 137export function callUseMutation<T = any, V extends AnyVariables = AnyVariables>( 138 query: MaybeRefOrGetter<DocumentInput<T, V>>, 139 client: Ref<Client> = useClient() 140): UseMutationResponse<T, V> { 141 const data: Ref<T | undefined> = shallowRef(); 142 143 const { fetching, operation, extensions, stale, error, hasNext } = 144 useRequestState<T, V>(); 145 146 return { 147 data, 148 stale, 149 fetching, 150 error, 151 operation, 152 extensions, 153 hasNext, 154 executeMutation( 155 variables: V, 156 context?: Partial<OperationContext> 157 ): Promise<OperationResult<T, V>> { 158 fetching.value = true; 159 160 return pipe( 161 client.value.executeMutation<T, V>( 162 createRequestWithArgs<T, V>({ query, variables }), 163 context || {} 164 ), 165 onPush(result => { 166 data.value = result.data; 167 stale.value = result.stale; 168 fetching.value = false; 169 error.value = result.error; 170 operation.value = result.operation; 171 extensions.value = result.extensions; 172 hasNext.value = result.hasNext; 173 }), 174 filter(result => !result.hasNext), 175 take(1), 176 toPromise 177 ); 178 }, 179 }; 180}