When to query data in a framework's loader vs a route component? Or imported component?
Coming from a Remix/RR7 background where basically all data fetching was done in the loader. Now I'm making headway in TanStack Start/Router and Convex and its slowly starting to click. Convex docs for TanStack always show doing a query in the route component, usually highlighting the reactivity for changes in the database which is great.
My questions are, under what circumstances would one do the query in a loader/beforeLoad and when would one do it in the route component? When reactivity is needed is it only achieved in the route component? Can reactivity happen from an imported component that has a useQuery in its export? If one doesn't need reactivity is it better to query in the loader?
Some of this has to do with the code splitting that TanStack does, keeping code securely processed on the server vs potentially exposed on the client. How that all works is one of the many ways my ignorance starts to show. Like anyone else, I want to keep safe the data that needs keeping safe, and also have the app do the work where it should be doing it (loader/component/route) and not mistakenly trying to make it do it one way when it only works the other.
13 Replies
Thanks for posting in <#1088161997662724167>.
Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets.
- Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.)
- Use search.convex.dev to search Docs, Stack, and Discord all at once.
- Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI.
- Avoid tagging staff unless specifically instructed.
Thank you!
Some of this has to do with the code splitting that TanStack does, keeping code securely processed on the server vs potentially exposed on the clientAt least for the parts of your app that use Convex, you're already safe here! Convex functions always only run on the convex deployment (the convex server).
under what circumstances would one do the query in a loader/beforeLoad and when would one do it in the route component?Anything in a loader runs during SSR and during a preload (by default, hover over the link to the page). So start loading in the loader if you want to get a head start on loading a query, or block with
await ensureQuery
in the loader if you never want your route to load without the query already being loaded.
When reactivity is needed is it only achieved in the route component?Yep, you don't get reactivity unless you use
useQuery()
.
Can reactivity happen from an imported component that has a useQuery in its export?I don't understand this, could you give an example? Any component that uses
useQuery()
with a Convex query will be reactive, it doesn't matter where you import it from.
If one doesn't need reactivity is it better to query in the loader?Loading is generally "better" in the loader, since you get to take advantage of preloading—whether you need reactivity or not. If you don't want reactivity, avoid using
useQuery()
, since that will keep the query live.What does it look like to subscribe to a dynamic route in the loader? Can't use useQuery because its a invalid hook call. I have it working non-reactive.
export const Route = createFileRoute("/events/$eventId")({
component: Event,
loader: async ({ context, params }) => {
return await context.queryClient.fetchQuery(
convexQuery(api.events.getEvent, {
eventId: params.eventId as Id<"events">,
}),
);
},
});
I'm scrubbing through your Building with TanStack Start and Convex Demo youtube looking for inspiration. 🙂Also see https://convex-tanstack-start.vercel.app/ for some info
subscribe to a dynamic route in the loader"Subscribing" usually means you want live updates, but loaders only run once, when the page loads. All you need to add to this is a
useQuery()
in the componentOops, word context. I was scrubbing through the video meaning quickly scanning.
I mean re
What does it look like to subscribe to a dynamic route in the loader?That's not something you can do because loaders only run once. You need to use useQuery() to subscribe to the changes
Oh man. I can't read today! Scribbing/Subscribe = mild dyslexia!
OK, so, if I can't call useQuery in the loader, and the Route const is where the params are called, how do I use useQuery to subscribe to a dynamic route with params? Do I do something like make a const evenId = params.eventId in the loader and pass that via useLoaderData to the component to then have it to use in UseQuery? or can I call params in the component also and not just up tine Route const?
here's an example https://github.com/get-convex/convex-tanstack-start/blob/8eb95a0f1a20f57ed9c26263b923c0e0870096ad/app/routes/react-query.tsx#L10-L25
oh no params, just a sec
Yeah, thats the fly in the ointment.
this is closer https://github.com/get-convex/convex-tanstack-start/blob/8eb95a0f1a20f57ed9c26263b923c0e0870096ad/app/routes/loaders/ensure.tsx
So forgetting loaders for a sec, you can access params in the component. Do a useQuery with that.
But then the fun thing about loaders is they let you preload, so that the useQuery is never
undefined
. You can await context.queryClient.ensureQueryData(...
to block the component from rendering until this data is available.
But either way, the way you load data in the component is the same, useQuery()OK, so how do I call/refer to params in the component? I'm going to try to figure it out first based on the example, but the example doesn't specifically use params
I think it's
Route.useSearch()
for query params, and Route.useParams()
for path parameters https://tanstack.com/router/v1/docs/framework/react/guide/path-params#path-params-in-componentsPerfect. Thanks for the direction.
OK, that's way too easy.