T
TanStack2y ago
vicious-gold

TRPC + React Query

I am using Tanstack router and query with TRPC. I am transitioning an application to use the new router. I am normally doing data fetching at the component with hooks using react query like so: const posts = trpc.posts.getPosts.useQuery(); You cannot use these hooks inside of the loader method for routes, so I am using the trpc proxy client to create a none react client to use, so I can use: loader: () => trpcVanillaClient.posts.getPosts.query() I am using both clients right now. I am using the vanillaClient to handle fetching data inside the route loader, while I am using the reactTrpc client to handle mutations so I can have access to the hooks inside the component. Here is an example of a route that is using both clients. trpcVanillaClient and trpcRect. I am invalidating the route's loader data after the mutation is successful
// The route using the vanilla trpc client
export const Route = new FileRoute("/").createRoute({
component: Home,
loader: () => trpcVanillaClient.posts.getPosts.query()
});

...
// The mutation in the component (using the reactTrpc client):
const createPostMutation = trpcReact.posts.createPost.useMutation({
onSuccess: () => {
router.invalidate();
}
});
// The route using the vanilla trpc client
export const Route = new FileRoute("/").createRoute({
component: Home,
loader: () => trpcVanillaClient.posts.getPosts.query()
});

...
// The mutation in the component (using the reactTrpc client):
const createPostMutation = trpcReact.posts.createPost.useMutation({
onSuccess: () => {
router.invalidate();
}
});
Is there a proper way to handle this? Are there any downsides to having both trpc clients? Thanks
No description
20 Replies
equal-aqua
equal-aqua2y ago
Edit: This needs the v11 alpha of tRPC. I come across the same thing, this is how I handled it:
// api.ts
import { QueryClient } from '@tanstack/react-query'
import { createTRPCQueryUtils, createTRPCReact } from '@trpc/react-query'
import type { AppRouter } from '../../../server/src'

const api = createTRPCReact<AppRouter>()
const queryClient = new QueryClient()
const client = api.createClient({
transformer: SuperJSON,
links: [
// ...
],
})

export const ApiProvider = //...

export const apiClient = api
export const apiUtils = createTRPCQueryUtils({ client, queryClient })
// api.ts
import { QueryClient } from '@tanstack/react-query'
import { createTRPCQueryUtils, createTRPCReact } from '@trpc/react-query'
import type { AppRouter } from '../../../server/src'

const api = createTRPCReact<AppRouter>()
const queryClient = new QueryClient()
const client = api.createClient({
transformer: SuperJSON,
links: [
// ...
],
})

export const ApiProvider = //...

export const apiClient = api
export const apiUtils = createTRPCQueryUtils({ client, queryClient })
// Usage:

import { apiClient, apiUtils } from '@/utils/api'

export const Route = new FileRoute('/').createRoute({
component: RouteComponent,
loader: apiUtils.posts.getPosts.fetch()
})
// Usage:

import { apiClient, apiUtils } from '@/utils/api'

export const Route = new FileRoute('/').createRoute({
component: RouteComponent,
loader: apiUtils.posts.getPosts.fetch()
})
Oh wait, I just noticed that I am using the v11 alpha of tRPC. I think this feature was the reason to use v11 already.
afraid-scarlet
afraid-scarlet2y ago
yoo what?? im checking this rn in my project. looks exactly like what I need. (using v11 too) 🫡
equal-aqua
equal-aqua2y ago
@Zion If you want to make use of react-query, you can also use this pattern. (Don't know where I saw this...) Although I haven't tried that myself.
export const Route = new FileRoute('/').createRoute({
component: RouteComponent,
loader: apiUtils.posts.getPosts.ensureData()
})

function RouteComponent() {
const { data } = apiClient.posts.getPosts.useQuery()
//...
}
export const Route = new FileRoute('/').createRoute({
component: RouteComponent,
loader: apiUtils.posts.getPosts.ensureData()
})

function RouteComponent() {
const { data } = apiClient.posts.getPosts.useQuery()
//...
}
afraid-scarlet
afraid-scarlet2y ago
yeah, I tried doing something like that before but couldnt because the utils were a hook, good to see theres a solution thanks 🙏
multiple-amethyst
multiple-amethyst2y ago
would you want to use the hooks though? i was looking at the kitchen sink example project using react query and it looks like it's using useSuspenseQuery to guarantee the data is there, and then basically prefetching in the route loader im think you can also await fetchQuery in the route loader (to block rendering and guarantee a success) since ideally you separate your loading component into the router rather than depending on if data is undefined or not? looking forward to hearing yalls thoughts i got my own version going over the holidays and just ended up going with use router.useLoaderData and returning the fetchQuery. it seems like both solutions work, my main concern is really dev education since on my team everyone is already used to just calling a useQuery and rendering based on isError, data, etc @Tanner Linsley you have thoughts here? 😬
afraid-scarlet
afraid-scarlet2y ago
createTRPCQueryUtils | tRPC
Similar to useUtils, createTRPCQueryUtils is a function that gives you access to helpers that let you manage the cached data of the queries you execute via @trpc/react-query. These helpers are actually thin wrappers around @tanstack/react-query's queryClient methods. If you want more in-depth information about options and usage patterns for useU...
afraid-scarlet
afraid-scarlet2y ago
this could help
afraid-scarlet
afraid-scarlet2y ago
No description
multiple-amethyst
multiple-amethyst2y ago
yeah thats what im using, just moreso posing the question of, do we even want to use useQuery at all with the way tsr works
afraid-scarlet
afraid-scarlet2y ago
ensure data doesnt fetch new data if theres already cache I think and you will lose a lot of abilities wihout the use query such as refetching
multiple-amethyst
multiple-amethyst2y ago
gotcha
afraid-scarlet
afraid-scarlet2y ago
theres no issue to use both as seen in the example
multiple-amethyst
multiple-amethyst2y ago
thanks! excited for v11 to come out
correct-apricot
correct-apricot2y ago
Sorry, what am I looking at?
multiple-amethyst
multiple-amethyst2y ago
if it makes sense to use useQuery with TSR because i figured the benefit of the loader prop is the route wont load until the promise has resolved in the loader
afraid-scarlet
afraid-scarlet2y ago
export const component = function SecretComponent() {
const { privateGreetingsData } = api.useLoaderData();
const { data, isPending } = trpc.test.privateGreetings.useQuery(undefined, {
initialData: privateGreetingsData,
});

return (
<div className="p-2">
<h3>SHHH!</h3>

{isPending ? (
<p>Loading...</p>
) : (
<>
<p>
Hello <code>{data}</code>
</p>
</>
)}


</div>
);
};
export const component = function SecretComponent() {
const { privateGreetingsData } = api.useLoaderData();
const { data, isPending } = trpc.test.privateGreetings.useQuery(undefined, {
initialData: privateGreetingsData,
});

return (
<div className="p-2">
<h3>SHHH!</h3>

{isPending ? (
<p>Loading...</p>
) : (
<>
<p>
Hello <code>{data}</code>
</p>
</>
)}


</div>
);
};
loader:
loader: async () => {
const privateGreetingsData =
await apiUtils.test.privateGreetings.ensureData();
return {
privateGreetingsData,
};
},
loader: async () => {
const privateGreetingsData =
await apiUtils.test.privateGreetings.ensureData();
return {
privateGreetingsData,
};
},
pretty much a full example
multiple-amethyst
multiple-amethyst2y ago
oh!! heck yea
correct-apricot
correct-apricot2y ago
Correct It's still beneficial to use both
multiple-amethyst
multiple-amethyst2y ago
thank you all!
stormy-gold
stormy-gold2y ago
Was about to use that pattern but then I saw tanner server functions tweet : https://twitter.com/tannerlinsley/status/1758605969674981584 It seems next/solid/tanstack all point to the same direction (server action/function). Do you plan to keep using trpc (mainly for exposing the api to other clients I guess) / and how server function could integrate with trpc?
Tanner Linsley (@tannerlinsley) on X
Server functions in @Tan_Stack are gonna be pretty dope.
From An unknown user
Twitter

Did you find this page helpful?