T
TanStack2y ago
other-emerald

Should I use Loader in routes?

If I'm using react-query should I integrate it to the router? Should I also migrate the whole react query custom hooks inside my components and move the logic into the loader using the queryClient?
9 Replies
initial-rose
initial-rose2y ago
It is a preference thing. If you want to use the loader for data fetching, then yes it would be wise to move away from the custom hooks approach. A good tool would be the queryOptions helper exported by Tanstack Query, that lets you create the input object you'd supply into functions like prefetchQuery and ensureQueryData as well as useQuery. https://tanstack.com/query/latest/docs/framework/react/reference/queryOptions
queryOptions | TanStack Query Docs
queryOptions({ queryKey,
initial-rose
initial-rose2y ago
TkDodo's article on the queryOptions API is a good read for this. https://tkdodo.eu/blog/the-query-options-api
The Query Options API
v5 brought a new, powerful API, especially if you're using React Query with TypeScript...
initial-rose
initial-rose2y ago
You could even still maintain your custom hooks, by just return the hook without the logic in it.
// all of these are NOT actually meant to be in the same file
const postsQuery = queryOptions({
queryKey: ['posts'],
queryFn: () => fetch('/posts'),
staleTime: 1000 * 60 * 5
});

const usePosts = () => useQuery(postsQuery)

const Route = createFileRoute("/posts")({
loader: async ({ context }) => {
await context.queryClient.ensureQueryData(postsQuery)
}
})
// all of these are NOT actually meant to be in the same file
const postsQuery = queryOptions({
queryKey: ['posts'],
queryFn: () => fetch('/posts'),
staleTime: 1000 * 60 * 5
});

const usePosts = () => useQuery(postsQuery)

const Route = createFileRoute("/posts")({
loader: async ({ context }) => {
await context.queryClient.ensureQueryData(postsQuery)
}
})
Or you could consume it purely from the route context without the custom hooks
export const Route = createFileRoute("/posts/$postId")({
beforeLoad: ({ params: { postId } }) => ({
postByIdOptions: queryOptions({
queryKey: ['posts', postId],
// ...
})
}),
loader: async ({ context }) => {
await context.queryClient.ensureQueryData(context.postByIdOptions)
},
component: Component
});

const api = getRouteApi("/posts/$postId")

const Component = () => {
const routeContext = api.useRouteContext();
const postByIdQuery = useSuspenseQuery(routeContext.postByIdOptions);
}
...
export const Route = createFileRoute("/posts/$postId")({
beforeLoad: ({ params: { postId } }) => ({
postByIdOptions: queryOptions({
queryKey: ['posts', postId],
// ...
})
}),
loader: async ({ context }) => {
await context.queryClient.ensureQueryData(context.postByIdOptions)
},
component: Component
});

const api = getRouteApi("/posts/$postId")

const Component = () => {
const routeContext = api.useRouteContext();
const postByIdQuery = useSuspenseQuery(routeContext.postByIdOptions);
}
...
other-emerald
other-emeraldOP2y ago
Ok I see. My queries options right now are not declared separately.. What the benefits of using the loader instead of using react query directly into the component? Route caching and preloading? For example hovering a link can trigger the loader and next route can load faster?
initial-rose
initial-rose2y ago
The preloading would be the biggest one IMO. By just hovering a link you can preload the data required, so a click near instantly should the UI to the user. Yes.
initial-rose
initial-rose2y ago
I have all the routes code-split with data loading done in the loaders. See the quick rendering. (FYI this is not on localhost)
other-emerald
other-emeraldOP2y ago
But I think that's not for all cases. Some routes may trigger more expensive calls at the back-end so you don't want to preload on hover. I think it would apply in a case of a entity preview.
initial-rose
initial-rose2y ago
Absolutely! This is entirely opt-in and you decide what data you want to be preloaded. You have full granular control over this process in-terms of what data is going to be fetched.
other-emerald
other-emeraldOP2y ago
Ok that's awesome. Thank you for making it clear to me.

Did you find this page helpful?