T
TanStack2mo ago
deep-jade

Router + Query + Suspense + Deferred data

Hello, I have a question about using Router + Query + Suspense + deferred data preload So I want to have a page with a critical data + deferred data (preferrably, multiple ones) while using Tanstack Query. It seems like the flow should be something like that: - User make a request, server runs loader that includes both critical data fetches and deferred data fetches - As long as critical data is retrieved, loaders returns with { deferred: queryClient.fetchQuery(...) } - Critical data is dehydrated (queryClient dehydrates completely) on server and hydrated on client (via createTanStackRouter.(de)hydrate functions) - Now, deferred data is delivered later, in a promise and client has to hydrate to the query client manually Basically, route looks like this:
const Route = createFileRoute("...")({
// ...
loader: async ({ context }) => {
const deferredDataPromise = context.queryClient.prefetchQuery(deferredQueryOptions());
const criticalData = await context.queryClient.fetchQuery(criticalQueryOptions());
return {
deferred: deferredDataPromise,
};
},
});
const Route = createFileRoute("...")({
// ...
loader: async ({ context }) => {
const deferredDataPromise = context.queryClient.prefetchQuery(deferredQueryOptions());
const criticalData = await context.queryClient.fetchQuery(criticalQueryOptions());
return {
deferred: deferredDataPromise,
};
},
});
The problem is how to hydrate deferred data properly on the client. The way I made it work is:
const loaderData = Route.useLoaderData();
const deferredData = React.use(loaderData.deferred);
const query = useSuspenseQuery({ ...deferredQueryOptions(), initialData: deferredData });
const loaderData = Route.useLoaderData();
const deferredData = React.use(loaderData.deferred);
const query = useSuspenseQuery({ ...deferredQueryOptions(), initialData: deferredData });
That's not ideal, I'd like to have just the last string and don't drill props if I need deferred data deeper in the tree.
3 Replies
deep-jade
deep-jadeOP2mo ago
One might expect route itself would have dehydrate / hydrate parameters that will run server / client-side respectively.
createFileRoute('...')({
dehydrate: ({ context }, { chunk }) => ({ dehydratedState: context.queryClient.dehydrate({...}) }),
hydrate: ({ context }, { dehydratedState }) => context.queryClient.hydrate(dehydratedState),
})
createFileRoute('...')({
dehydrate: ({ context }, { chunk }) => ({ dehydratedState: context.queryClient.dehydrate({...}) }),
hydrate: ({ context }, { dehydratedState }) => context.queryClient.hydrate(dehydratedState),
})
In this case the main problem is how to signal useSuspenseQuery that we need to await given promise (should initialData have an option to be a promise for useSuspenseQuery?)
eastern-cyan
eastern-cyan2mo ago
we have a package for that @tanstack/react-router-with-query
eastern-cyan
eastern-cyan2mo ago
and it is getting quite an upgrade in https://github.com/TanStack/router/pull/4600
GitHub
feat: use seroval for SSR by schiller-manuel · Pull Request #4600 ...
also stream all queries in react-router-with-query

Did you find this page helpful?