Skip to content

Commit

Permalink
feat: useMutation composable
Browse files Browse the repository at this point in the history
  • Loading branch information
wobsoriano committed Dec 7, 2023
1 parent d430c64 commit f45957b
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 6 deletions.
13 changes: 8 additions & 5 deletions playground/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,23 @@ const { data } = useNuxtData(todosKey)
const { data: todos, pending, error, refresh } = await $client.todo.getTodos.useQuery()
const { mutate } = $client.todo.addTodo.useMutation()
const addTodo = async () => {
const title = Math.random().toString(36).slice(2, 7)
const newData = {
id: Date.now(),
userId: 69,
title,
completed: false
}
data.value.push(newData)
try {
const x = await $client.todo.addTodo.mutate(newData)
} catch (e) {
console.log(e)
}
await mutate(newData)
await refreshNuxtData(todosKey)
}
</script>

Expand Down
39 changes: 38 additions & 1 deletion src/client/decorationProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type inferRouterProxyClient } from '@trpc/client'
import { type AnyRouter } from '@trpc/server'
import { createRecursiveProxy } from '@trpc/server/shared'
// @ts-expect-error: Nuxt auto-imports
import { getCurrentInstance, onScopeDispose, useAsyncData, unref, isRef } from '#imports'
import { getCurrentInstance, onScopeDispose, useAsyncData, unref, ref, isRef, toRaw } from '#imports'
import { getQueryKeyInternal } from './getQueryKey'

export function createNuxtProxyDecoration<TRouter extends AnyRouter> (name: string, client: inferRouterProxyClient<TRouter>) {
Expand Down Expand Up @@ -52,6 +52,43 @@ export function createNuxtProxyDecoration<TRouter extends AnyRouter> (name: stri
lazy: isLazy
})
}

if (lastArg === 'useMutation') {
const { trpc, queryKey: customQueryKey, ...asyncDataOptions } = otherOptions || {} as any
// Payload will be set by the `mutate` function and used by `useAsyncData`.
const payload = ref(null)

let controller: AbortController

if (trpc?.abortOnUnmount) {
if (getCurrentInstance()) {
onScopeDispose(() => {
controller?.abort?.()
})
}
controller = typeof AbortController !== 'undefined' ? new AbortController() : {} as AbortController
}

const asyncData = useAsyncData(() => (client as any)[path].mutate(payload.value, {
signal: controller?.signal,
...trpc
}), {
...asyncDataOptions,
immediate: false
})

// eslint-disable-next-line no-inner-declarations
async function mutate (input: any) {
payload.value = input
await asyncData.execute()
return toRaw(asyncData.data.value)
}

return {
mutate,
...asyncData
}
}

return (client as any)[path][lastArg](...args)
})
Expand Down
10 changes: 10 additions & 0 deletions src/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ export type DecorateProcedure<
query: Resolver<TProcedure>
} : TProcedure extends AnyMutationProcedure ? {
mutate: Resolver<TProcedure>
useMutation: <
ResT = inferTransformedProcedureOutput<TProcedure>,
DataE = TRPCClientErrorLike<TProcedure>,
DataT = ResT,
PickKeys extends KeysOf<DataT> = KeysOf<DataT>,
>(
opts?: Omit<AsyncDataOptions<ResT, DataT, PickKeys>, 'lazy'> & {
trpc?: TRPCRequestOptions
},
) => AsyncData<PickFrom<DataT, PickKeys> | null, DataE> & { mutate: (input: inferProcedureInput<TProcedure>) => Promise<AsyncData<PickFrom<DataT, PickKeys> | null, DataE>['data']> },
} : TProcedure extends AnySubscriptionProcedure ? {
subscribe: SubscriptionResolver<TProcedure, TRouter>
} : never
Expand Down

0 comments on commit f45957b

Please sign in to comment.