|
Hello, I’m learning TanStack Query. Situation:
Question: Relevant Code: page.tsx client component queryOptions ServerFetchBoundary.tsx queryClientOption Is there a recommended way to handle prefetchInfiniteQuery in Server Components so that the client can safely continue infiniteQuery fetching even when the server throws an error? Thank you for your help! |
Replies: 3 comments 1 reply
|
streaming doesn’t yet work with infinite queries: |
|
The root cause here is that your Here's what happens step by step:
The fix: await your prefetches and catch auth failures Update export const ServerFetchBoundary = async ({
children,
queryOptions,
infiniteQueryOptions,
}: ServerFetchBoundaryProps) => {
await connection();
const queryClient = getQueryClient();
const queries = queryOptions
? Array.isArray(queryOptions) ? queryOptions : [queryOptions]
: [];
const infiniteQueries = infiniteQueryOptions
? Array.isArray(infiniteQueryOptions) ? infiniteQueryOptions : [infiniteQueryOptions]
: [];
await Promise.all([
...queries.map(opt => queryClient.prefetchQuery(opt).catch(() => {})),
...infiniteQueries.map(opt => queryClient.prefetchInfiniteQuery(opt).catch(() => {})),
]);
return <HydrationBoundary state={dehydrate(queryClient)}>{children}</HydrationBoundary>;
};The
This avoids the streaming path entirely for all these queries, which is exactly TkDodo's recommended workaround: "don't stream promises for infinite queries, but await them on the server." Why not just make getNextPageParam: (lastPage: FeedListResponse | undefined) =>
lastPage?.hasNext ? lastPage.cursor : undefined,This silences the crash but doesn't fix the underlying data structure problem — the client is still working with a malformed query state that'll cause other subtle issues. The upstream fix for the streaming case is being tracked in PR #9633 if you need that path later. |
Resolving the
|
The root cause here is that your
ServerFetchBoundaryisn't awaiting the prefetches, which triggers the streaming path that's broken for infinite queries (the bug TkDodo linked).Here's what happens step by step:
infiniteQueries.forEach(opt => queryClient.prefetchInfiniteQuery(opt))fires the prefetch but doesn't await it, so the query stays inpendingstate whendehydrate(queryClient)runs|| query.state.status === 'pending'inshouldDehydrateQuery, the pending promise gets dehydrated and sent to the clientquery.fetch(undefined, { initialPromise })to pick up the stream — but critically, it does not attachinfiniteQueryBehaviorbecause hyd…