Skip to content

Comments

Prevent useQuery from skipping hydration#10159

Open
ColemanDunn wants to merge 3 commits intoTanStack:mainfrom
ColemanDunn:prevent-usequery-from-blocking-hydration
Open

Prevent useQuery from skipping hydration#10159
ColemanDunn wants to merge 3 commits intoTanStack:mainfrom
ColemanDunn:prevent-usequery-from-blocking-hydration

Conversation

@ColemanDunn
Copy link
Contributor

@ColemanDunn ColemanDunn commented Feb 21, 2026

🎯 Changes

Fixes #10145

Hydration is being skipped for useQueries that add to the cache above a HydrationBoundary but will actually not run or hydrate the queryClient.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • Bug Fixes

    • Hydration now includes idle/pending queries to avoid unnecessary refetches or suspense-triggered reloads.
  • New Features

    • Added two public APIs: defaultShouldDehydrateQuery and useSuspenseQuery.
  • Tests

    • Added coverage for idle-query hydration behavior inside Suspense boundaries.
  • Chores

    • Published patch updates for two TanStack libraries.

@changeset-bot
Copy link

changeset-bot bot commented Feb 21, 2026

🦋 Changeset detected

Latest commit: 36edbba

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 20 packages
Name Type
@tanstack/react-query Patch
@tanstack/query-core Patch
@tanstack/react-query-devtools Patch
@tanstack/react-query-next-experimental Patch
@tanstack/react-query-persist-client Patch
@tanstack/angular-query-experimental Patch
@tanstack/preact-query Patch
@tanstack/query-async-storage-persister Patch
@tanstack/query-broadcast-client-experimental Patch
@tanstack/query-persist-client-core Patch
@tanstack/query-sync-storage-persister Patch
@tanstack/solid-query Patch
@tanstack/svelte-query Patch
@tanstack/vue-query Patch
@tanstack/angular-query-persist-client Patch
@tanstack/solid-query-persist-client Patch
@tanstack/svelte-query-persist-client Patch
@tanstack/solid-query-devtools Patch
@tanstack/svelte-query-devtools Patch
@tanstack/vue-query-devtools Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 21, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Hydration logic was changed so existing idle/pending queries (dataUpdatedAt === 0, status === "pending", fetchStatus === "idle") are treated like absent queries during hydration; a changeset bumps react-query and query-core as patch releases and tests added to verify no suspense-triggered refetch occurs.

Changes

Cohort / File(s) Summary
Changeset Entry
.changeset/moody-cities-stand.md
Adds a changeset bumping @tanstack/react-query and @tanstack/query-core (patch) with note to prevent registered useQueries from skipping hydration.
Query-core hydration logic
packages/query-core/src/hydration.ts
Introduces existingQueryIsUndefinedOrIsIdleUseQuery condition and updates hydration checks so existing idle/pending queries are eligible for hydration (adjusts promise hydration trigger and preservation of dehydration ordering).
React Hydration boundary
packages/react-query/src/HydrationBoundary.tsx
Adds condition to treat existing queries with dataUpdatedAt===0, status==='pending', fetchStatus==='idle' as candidates for hydration, pushing them into the hydration flow.
Tests / Public exports
packages/react-query/src/__tests__/HydrationBoundary.test.tsx
Adds tests that validate hydrating pending-idle queries in a Suspense render to avoid refetch; re-exports used helpers/hooks for test scenarios.

Sequence Diagram(s)

sequenceDiagram
    participant Server
    participant HydrationBoundary
    participant QueryClient
    participant ClientComponent

    Server->>QueryClient: prefetchQuery(key, data)
    Server->>HydrationBoundary: provide dehydrated state
    ClientComponent->>HydrationBoundary: render useQuery / useSuspenseQuery
    HydrationBoundary->>QueryClient: check existingQuery state
    alt existingQuery missing OR idle/pending (dataUpdatedAt==0, pending, idle)
        HydrationBoundary->>QueryClient: hydrate dehydratedQuery -> replace/cache data
    else existingQuery active/fetching
        HydrationBoundary->>QueryClient: skip hydration for this key
    end
    ClientComponent->>QueryClient: read cached data (no suspense refetch)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • TkDodo
  • manudeli

Poem

🐇 I hopped where dehydrated values lay,
Found sleepy queries not quite awake today.
I nudged them gently into cached embrace,
No duplicate fetches, no frantic race.
Carrot cheers — hydration saved the place.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Prevent useQuery from skipping hydration' directly addresses the primary change, clearly summarizing the fix for the hydration skipping bug.
Description check ✅ Passed The description follows the template with all required sections completed: 🎯 Changes section references the linked issue #10145 and describes the bug, checklist items are marked complete, and 🚀 Release Impact indicates a changeset was generated.
Linked Issues check ✅ Passed The code changes successfully address issue #10145 by modifying hydration logic to treat pending idle queries as candidates for hydration, preventing skipping of server-prefetched queries when parent useQuery renders earlier.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the hydration issue: hydration.ts updates core logic, HydrationBoundary.tsx applies the fix in React implementation, and test additions validate the scenario from issue #10145.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ColemanDunn ColemanDunn force-pushed the prevent-usequery-from-blocking-hydration branch from 5fedd07 to 45eb471 Compare February 21, 2026 08:34
@nx-cloud
Copy link

nx-cloud bot commented Feb 21, 2026

View your CI Pipeline Execution ↗ for commit 36edbba

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 2m 33s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1s View ↗

☁️ Nx Cloud last updated this comment at 2026-02-21 08:52:06 UTC

@ColemanDunn ColemanDunn force-pushed the prevent-usequery-from-blocking-hydration branch from 24dbb40 to 133a6ca Compare February 21, 2026 08:37
@ColemanDunn ColemanDunn force-pushed the prevent-usequery-from-blocking-hydration branch from 133a6ca to de84710 Compare February 21, 2026 08:40
@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 21, 2026

More templates

@tanstack/angular-query-experimental

npm i https://pkg.pr.new/@tanstack/angular-query-experimental@10159

@tanstack/eslint-plugin-query

npm i https://pkg.pr.new/@tanstack/eslint-plugin-query@10159

@tanstack/preact-query

npm i https://pkg.pr.new/@tanstack/preact-query@10159

@tanstack/query-async-storage-persister

npm i https://pkg.pr.new/@tanstack/query-async-storage-persister@10159

@tanstack/query-broadcast-client-experimental

npm i https://pkg.pr.new/@tanstack/query-broadcast-client-experimental@10159

@tanstack/query-core

npm i https://pkg.pr.new/@tanstack/query-core@10159

@tanstack/query-devtools

npm i https://pkg.pr.new/@tanstack/query-devtools@10159

@tanstack/query-persist-client-core

npm i https://pkg.pr.new/@tanstack/query-persist-client-core@10159

@tanstack/query-sync-storage-persister

npm i https://pkg.pr.new/@tanstack/query-sync-storage-persister@10159

@tanstack/react-query

npm i https://pkg.pr.new/@tanstack/react-query@10159

@tanstack/react-query-devtools

npm i https://pkg.pr.new/@tanstack/react-query-devtools@10159

@tanstack/react-query-next-experimental

npm i https://pkg.pr.new/@tanstack/react-query-next-experimental@10159

@tanstack/react-query-persist-client

npm i https://pkg.pr.new/@tanstack/react-query-persist-client@10159

@tanstack/solid-query

npm i https://pkg.pr.new/@tanstack/solid-query@10159

@tanstack/solid-query-devtools

npm i https://pkg.pr.new/@tanstack/solid-query-devtools@10159

@tanstack/solid-query-persist-client

npm i https://pkg.pr.new/@tanstack/solid-query-persist-client@10159

@tanstack/svelte-query

npm i https://pkg.pr.new/@tanstack/svelte-query@10159

@tanstack/svelte-query-devtools

npm i https://pkg.pr.new/@tanstack/svelte-query-devtools@10159

@tanstack/svelte-query-persist-client

npm i https://pkg.pr.new/@tanstack/svelte-query-persist-client@10159

@tanstack/vue-query

npm i https://pkg.pr.new/@tanstack/vue-query@10159

@tanstack/vue-query-devtools

npm i https://pkg.pr.new/@tanstack/vue-query-devtools@10159

commit: 36edbba

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/react-query/src/HydrationBoundary.tsx (1)

76-79: Consider merging the two newQueries.push branches.

Both branches push to the same array. Combining them simplifies the control flow.

♻️ Optional simplification
-          if (!existingQuery) {
-            newQueries.push(dehydratedQuery)
-          } else if (existingQueryIsIdleUseQuery) {
+          if (!existingQuery || existingQueryIsIdleUseQuery) {
             newQueries.push(dehydratedQuery)
           } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-query/src/HydrationBoundary.tsx` around lines 76 - 79, The two
branches that both call newQueries.push(dehydratedQuery) can be merged: replace
the separate if (!existingQuery) { newQueries.push(dehydratedQuery) } else if
(existingQueryIsIdleUseQuery) { newQueries.push(dehydratedQuery) } with a single
conditional that pushes when either condition is true (e.g., if (!existingQuery
|| existingQueryIsIdleUseQuery) newQueries.push(dehydratedQuery)); update the
logic around existingQuery and existingQueryIsIdleUseQuery so no behavior
changes occur beyond the simplification.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.changeset/moody-cities-stand.md:
- Line 6: Fix the typo "hyrdation" to "hydration" in the generated changelog
text by updating the string "prevent registered useQueries from skipping
hyrdation" to "prevent registered useQueries from skipping hydration" (search
for the exact misspelled token "hyrdation" in the changelog entry and replace
it).

---

Nitpick comments:
In `@packages/react-query/src/HydrationBoundary.tsx`:
- Around line 76-79: The two branches that both call
newQueries.push(dehydratedQuery) can be merged: replace the separate if
(!existingQuery) { newQueries.push(dehydratedQuery) } else if
(existingQueryIsIdleUseQuery) { newQueries.push(dehydratedQuery) } with a single
conditional that pushes when either condition is true (e.g., if (!existingQuery
|| existingQueryIsIdleUseQuery) newQueries.push(dehydratedQuery)); update the
logic around existingQuery and existingQueryIsIdleUseQuery so no behavior
changes occur beyond the simplification.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/query-core/src/hydration.ts (1)

220-224: existingQueryIsUndefinedOrIsIdleUseQuery — naming couples implementation to a specific caller

The three-condition guard (dataUpdatedAt === 0 && status === 'pending' && fetchStatus === 'idle') characterises a structural query state (never-fetched and idle), not the type of observer that created the entry. The UseQuery suffix implies caller-origin knowledge that isn't actually verified here. A name like existingQueryIsUndefinedOrNeverFetchedIdle or queryIsAbsentOrIdlePending would be less misleading to future readers.

♻️ Suggested rename
-      const existingQueryIsUndefinedOrIsIdleUseQuery =
+      const existingQueryIsAbsentOrNeverFetchedIdle =
         !query ||
         (query.state.dataUpdatedAt === 0 &&
           query.state.status === 'pending' &&
           query.state.fetchStatus === 'idle')

And update the usage at line 270:

-        (existingQueryIsUndefinedOrIsIdleUseQuery ||
+        (existingQueryIsAbsentOrNeverFetchedIdle ||
           (!existingQueryIsPending && !existingQueryIsFetching)) &&
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/query-core/src/hydration.ts` around lines 220 - 224, Rename the
misleading variable existingQueryIsUndefinedOrIsIdleUseQuery to a name that
reflects the structural state check (for example
existingQueryIsUndefinedOrNeverFetchedIdle or queryIsAbsentOrIdlePending) and
update all references (including the use at the site that currently reads the
old name) so the code checks the same three-condition guard (query is falsy OR
query.state.dataUpdatedAt === 0 && query.state.status === 'pending' &&
query.state.fetchStatus === 'idle') but without implying a specific caller like
UseQuery; update both the variable declaration and every place it is referenced
(e.g., the expression currently using existingQueryIsUndefinedOrIsIdleUseQuery).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/query-core/src/hydration.ts`:
- Around line 220-224: Rename the misleading variable
existingQueryIsUndefinedOrIsIdleUseQuery to a name that reflects the structural
state check (for example existingQueryIsUndefinedOrNeverFetchedIdle or
queryIsAbsentOrIdlePending) and update all references (including the use at the
site that currently reads the old name) so the code checks the same
three-condition guard (query is falsy OR query.state.dataUpdatedAt === 0 &&
query.state.status === 'pending' && query.state.fetchStatus === 'idle') but
without implying a specific caller like UseQuery; update both the variable
declaration and every place it is referenced (e.g., the expression currently
using existingQueryIsUndefinedOrIsIdleUseQuery).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HydrationBoundary ignores/skips server-prefetched detail query when a parent useQuery with same key is rendered before child suspense fetch

1 participant