diff --git a/packages/query-core/src/hydration.ts b/packages/query-core/src/hydration.ts index b99f507997..6191f7f1e8 100644 --- a/packages/query-core/src/hydration.ts +++ b/packages/query-core/src/hydration.ts @@ -142,10 +142,17 @@ export function hydrate( queries.forEach((dehydratedQuery) => { const query = queryCache.get(dehydratedQuery.queryHash) + // Reset fetch status to idle in the dehydrated state to avoid + // query being stuck in fetching state upon hydration + const dehydratedQueryState = { + ...dehydratedQuery.state, + fetchStatus: 'idle' as const, + } + // Do not hydrate if an existing query exists with newer data if (query) { - if (query.state.dataUpdatedAt < dehydratedQuery.state.dataUpdatedAt) { - query.setState(dehydratedQuery.state) + if (query.state.dataUpdatedAt < dehydratedQueryState.dataUpdatedAt) { + query.setState(dehydratedQueryState) } return } @@ -158,7 +165,7 @@ export function hydrate( queryKey: dehydratedQuery.queryKey, queryHash: dehydratedQuery.queryHash, }, - dehydratedQuery.state, + dehydratedQueryState, ) }) } diff --git a/packages/query-core/src/tests/hydration.test.tsx b/packages/query-core/src/tests/hydration.test.tsx index 1debd880b6..f4c863936e 100644 --- a/packages/query-core/src/tests/hydration.test.tsx +++ b/packages/query-core/src/tests/hydration.test.tsx @@ -426,4 +426,44 @@ describe('dehydration and rehydration', () => { queryClient.clear() }) + + test('should set the fetchStatus to idle in all cases when dehydrating', async () => { + const queryCache = new QueryCache() + const queryClient = createQueryClient({ queryCache }) + + let isInitialFetch = true + let resolvePromise: (value: unknown) => void = () => undefined + + const customFetchData = () => { + const promise = new Promise((resolve) => { + resolvePromise = resolve + }) + // Resolve the promise in initial fetch + // because we are awaiting the query first time + if (isInitialFetch) { + resolvePromise('string') + } + isInitialFetch = false + return promise + } + + await queryClient.prefetchQuery(['string'], () => customFetchData()) + + queryClient.refetchQueries(['string']) + + const dehydrated = dehydrate(queryClient) + resolvePromise('string') + expect( + dehydrated.queries.find((q) => q.queryHash === '["string"]')?.state + .fetchStatus, + ).toBe('fetching') + const stringified = JSON.stringify(dehydrated) + + // --- + const parsed = JSON.parse(stringified) + const hydrationCache = new QueryCache() + const hydrationClient = createQueryClient({ queryCache: hydrationCache }) + hydrate(hydrationClient, parsed) + expect(hydrationCache.find(['string'])?.state.fetchStatus).toBe('idle') + }) })