G$Row
G$Row7mo ago

How to get data type from onMutate function in useMutation

I'm doing optimistic updates using the technique which is documented here https://tanstack.com/query/latest/docs/framework/react/guides/optimistic-updates . I found that I'm using the same queries in multiple spots and want to extract the update code so that I don't have to copy and paste. I can't figure out the type of the data parameter in the onMutate function.
api.task.createTask.useMutation({
onMutate: async (data) => {}})
api.task.createTask.useMutation({
onMutate: async (data) => {}})
I've tried using type MutateData = Parameters<typeof api.task.createTask.useMutation>[0] . The problem is that I can't then access the ['onMutate'] because it doesn't exist on there. Does anyone know how to get this?
Optimistic Updates | TanStack Query React Docs
React Query provides two ways to optimistically update your UI before a mutation has completed. You can either use the onMutate option to update your cache directly, or leverage the returned variables to update your UI from the useMutation result. Via the UI
12 Replies
Mika
Mika7mo ago
Inferring Types | tRPC
It is often useful to access the types of your API within your clients. For this purpose, you are able to infer the types contained in your AppRouter.
Mika
Mika7mo ago
Specifically,
No description
G$Row
G$RowOP7mo ago
That seems to do it! Thanks! Can it be guaranteed that using the inferRouterInputs<typeof taskRouter>['createTask'] (in my case) will always be the same type as what is passed into that onMutate function ?
Emmytobs
Emmytobs6mo ago
Hi, @G$Row, I just started building a project using tRPC with Next.js and I ran into this exact issue. I did some Googling and found this chat, so I figured I'd reach out. I followed the link @Mika posted but I get a TypeScript error. Here's my procedure.
No description
Emmytobs
Emmytobs6mo ago
Here's the TypeScript error I get. Please if any of you have come across this error, I'd appreciate it if you could explain what might be causing it.
No description
Emmytobs
Emmytobs6mo ago
Here's the list of my dependencies
No description
Mika
Mika6mo ago
You will keep seeing these kinds of errors and even more frustrating errors because you define the mutation function somewhere else This means that you completely lose the context type information, which you tried to recover by inferring the router inputs You should inline your functions and keep the logic inside the router file
Emmytobs
Emmytobs6mo ago
Thanks so much for responding. That's definitely a way to avoid the error. The only reason I wanted the put the mutation function in a separate file is just for better organization - the router file could get really long if I have lots of procedures.
Mika
Mika6mo ago
Try not to fall into premature optimization trap. This is the perfect example of premature optimization When it gets big, you'll refactor it For now you're just making your life harder by letting typescript stand in the way between you and the app that you're building FWIW, I have a production app running with dozens of routers, which in turn have dozens of procedures inside them. No big deal .
Emmytobs
Emmytobs6mo ago
Gotcha. There's definitely a good reason behind starting out simple and refactoring as the project gets bigger. Since you shared the type inference on tRPC's website and I couldn't replicate it without the annoying TS error, I just thought I'd point it out to let you know. My guess is it's likely something that has to do with my TS version (which was why I shared my list of dependencies) I'll keep the procedures in the router file - and would try to keep an eye out for a solution.
Mika
Mika6mo ago
It's not a ts bug, let me try to explain The error you're getting is correct, this is a circular definition When you write RouterInputs["auth"]["login"] typescript goes to the procedure and tries to read it's definition But the auth login points to a function It's hard to explain but if you think about it, it makes sense When you try to retrieve the router inputs, typescript has to analyze the entire definition of that router, which includes the function definition ... It won't just read the .input(loginSchema) and be like ok, I'm done, here you go If you really want to solve this problem your way, change your type to the following
type loginOpts = {
input: z.infer<typeof loginSchema>
}
type loginOpts = {
input: z.infer<typeof loginSchema>
}
But then you'll lose everything else in tRPC context, and you'll get a typescript error saying that context type is not compatible with loginOpts..
Emmytobs
Emmytobs6mo ago
Oh, that makes sense. Just one of the interesting behaviors of TS, i guess. Thanks for explaining!