Skip to content

Conversation

@sukvvon
Copy link
Contributor

@sukvvon sukvvon commented Dec 14, 2025

🎯 Changes

Fixes #9610

When using HydrationBoundary with existing queries in the cache, an unnecessary refetch was triggered during hydration even though fresh data was about to be hydrated from the server.

Root Cause

  • useQuery's onSubscribe runs before HydrationBoundary's useEffect completes hydration
  • The query appears stale (before hydration), triggering an unnecessary refetch

Solution

  • Added pendingHydrationQueries WeakSet to track queries pending hydration
  • Mark existing queries as pending in useMemo (before children render)
  • Skip refetch in queryObserver.onSubscribe if query is pending hydration
  • Exception: refetchOnMount: 'always' still triggers refetch (user explicitly wants fresh data)
  • Clear pending flag in useEffect after hydration completes
  • Added cleanup function to clear pending flags on unmount

Files Changed

  • packages/query-core/src/hydration.ts - Export pendingHydrationQueries WeakSet
  • packages/query-core/src/index.ts - Re-export pendingHydrationQueries
  • packages/query-core/src/queryObserver.ts - Check pending hydration in onSubscribe
  • packages/react-query/src/HydrationBoundary.tsx - Mark/clear pending hydration queries with cleanup
  • packages/react-query/src/__tests__/HydrationBoundary.test.tsx - Add 14 test cases
  • docs/framework/react/guides/ssr.md - Document new behavior
  • docs/framework/react/reference/hydration.md - Document new behavior

✅ 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

    • HydrationBoundary now prevents unnecessary refetches during hydration, avoiding double-fetch on page load.
  • Documentation

    • Clarified SSR and hydration guides to explain hydration-aware behavior: hydrated queries won't trigger an initial background refetch unless refetchOnMount is set to "always".
  • Tests

    • Added comprehensive tests covering hydration scenarios, refetchOnMount variations, concurrent queries, cleanup on unmount, and edge cases to ensure stable hydration behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot
Copy link

changeset-bot bot commented Dec 14, 2025

🦋 Changeset detected

Latest commit: 653df2c

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

This PR includes changesets to release 19 packages
Name Type
@tanstack/query-core Patch
@tanstack/react-query Patch
@tanstack/angular-query-experimental 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/react-query-devtools Patch
@tanstack/react-query-next-experimental Patch
@tanstack/react-query-persist-client 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 Dec 14, 2025

📝 Walkthrough

Walkthrough

Adds hydration-aware tracking to prevent double-fetch during HydrationBoundary hydration: introduces a WeakSet to mark pending queries, updates QueryObserver to skip fetches for pending queries (unless forced), updates HydrationBoundary to mark/clear pending queries, adds docs, tests, and a changeset.

Changes

Cohort / File(s) Summary
Core Hydration Tracking
packages/query-core/src/hydration.ts, packages/query-core/src/index.ts
Add and export pendingHydrationQueries: WeakSet<Query> to track queries awaiting hydration.
Query Observer Hydration Awareness
packages/query-core/src/queryObserver.ts
Import pendingHydrationQueries; compute hydration state in onSubscribe and skip initial fetch when a query is pending hydration (respecting refetchOnMount: 'always').
HydrationBoundary Implementation
packages/react-query/src/HydrationBoundary.tsx
Mark dehydrated queries as pending during render; clear pending flags after hydration/effect and on unmount to avoid leaks.
Hydration Tests
packages/react-query/src/__tests__/HydrationBoundary.test.tsx
Add comprehensive tests covering hydration timing, various refetchOnMount modes, concurrent queries, removals during hydration, and cleanup on unmount.
Documentation
docs/framework/react/guides/ssr.md, docs/framework/react/reference/hydration.md
Clarify that HydrationBoundary prevents unnecessary refetch during hydration unless refetchOnMount is 'always'.
Changeset
.changeset/fix-hydration-double-fetch.md
Add changeset noting patch/patch bumps and the hydration refetch fix.

Sequence Diagram(s)

sequenceDiagram
    participant App as App / Router
    participant HB as HydrationBoundary
    participant QC as QueryClient / Cache
    participant QO as QueryObserver
    participant Net as Network

    rect rgb(230,240,255)
    Note over App,HB: Render phase (sync)
    App->>HB: Render with dehydrated state
    HB->>QC: Hydrate cache (useMemo)
    HB->>QC: For each hydrated query: add to pendingHydrationQueries
    Note over HB,QC: Queries flagged as pending
    end

    rect rgb(220,255,230)
    Note over QO,Net: Subscription / mount
    QO->>QO: onSubscribe runs
    QO->>QC: Check pendingHydrationQueries for query
    alt pending and refetchOnMount != 'always'
        QO->>QO: Skip initial fetch (avoid double-fetch)
    else not pending or refetchOnMount == 'always'
        QO->>Net: Fetch data
    end
    end

    rect rgb(255,250,220)
    Note over HB: Effect phase (async)
    HB->>HB: useEffect runs after paint
    HB->>QC: Complete hydration
    HB->>QC: clear pendingHydrationQueries for hydrated queries
    Note over HB,QC: Cleanup ensures no stale pending flags
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • manudeli
  • TkDodo

Poem

🐰 Soft paws on code at break of day,
I nibble double-fetches all away.
A WeakSet holds each sleepy query's name,
Hydration hops once — no server does the same! ✨

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: preventing unnecessary refetch during hydration in HydrationBoundary.
Description check ✅ Passed The description follows the template with detailed changes, completed checklist, and release impact declaration.
Linked Issues check ✅ Passed The PR successfully addresses issue #9610 by implementing the core solution: preventing unnecessary refetch during hydration using pendingHydrationQueries tracking.
Out of Scope Changes check ✅ Passed All changes are directly aligned with fixing the double-fetch issue: core hydration tracking, observer logic, boundary implementation, tests, and documentation updates.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9d64cf1 and 607f405.

📒 Files selected for processing (1)
  • .changeset/fix-hydration-double-fetch.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • .changeset/fix-hydration-double-fetch.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Preview
  • GitHub Check: Test

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

@github-actions github-actions bot added documentation Improvements or additions to documentation package: react-query package: query-core labels Dec 14, 2025
@nx-cloud
Copy link

nx-cloud bot commented Dec 14, 2025

View your CI Pipeline Execution ↗ for commit 653df2c

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

☁️ Nx Cloud last updated this comment at 2025-12-31 13:32:53 UTC

@sukvvon sukvvon force-pushed the fix/hydration-boundary-double-fetch branch from 86745a3 to 8626d42 Compare December 14, 2025 17:56
@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 14, 2025

More templates

@tanstack/angular-query-experimental

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

@tanstack/eslint-plugin-query

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

@tanstack/query-async-storage-persister

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

@tanstack/query-broadcast-client-experimental

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

@tanstack/query-core

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

@tanstack/query-devtools

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

@tanstack/query-persist-client-core

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

@tanstack/query-sync-storage-persister

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

@tanstack/react-query

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

@tanstack/react-query-devtools

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

@tanstack/react-query-next-experimental

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

@tanstack/react-query-persist-client

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

@tanstack/solid-query

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

@tanstack/solid-query-devtools

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

@tanstack/solid-query-persist-client

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

@tanstack/svelte-query

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

@tanstack/svelte-query-devtools

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

@tanstack/svelte-query-persist-client

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

@tanstack/vue-query

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

@tanstack/vue-query-devtools

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

commit: 653df2c

@codecov
Copy link

codecov bot commented Dec 14, 2025

Codecov Report

❌ Patch coverage is 95.45455% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 59.73%. Comparing base (f616474) to head (653df2c).

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##             main    #9968       +/-   ##
===========================================
+ Coverage   45.82%   59.73%   +13.90%     
===========================================
  Files         200      129       -71     
  Lines        8525     5760     -2765     
  Branches     1977     1596      -381     
===========================================
- Hits         3907     3441      -466     
+ Misses       4158     2002     -2156     
+ Partials      460      317      -143     
Components Coverage Δ
@tanstack/angular-query-experimental 93.85% <ø> (ø)
@tanstack/eslint-plugin-query ∅ <ø> (∅)
@tanstack/query-async-storage-persister 43.85% <ø> (ø)
@tanstack/query-broadcast-client-experimental 24.39% <ø> (ø)
@tanstack/query-codemods ∅ <ø> (∅)
@tanstack/query-core 97.33% <88.88%> (-0.05%) ⬇️
@tanstack/query-devtools 3.38% <ø> (ø)
@tanstack/query-persist-client-core 80.00% <ø> (ø)
@tanstack/query-sync-storage-persister 84.61% <ø> (ø)
@tanstack/query-test-utils ∅ <ø> (∅)
@tanstack/react-query 96.55% <100.00%> (+0.14%) ⬆️
@tanstack/react-query-devtools 9.25% <ø> (ø)
@tanstack/react-query-next-experimental ∅ <ø> (∅)
@tanstack/react-query-persist-client 100.00% <ø> (ø)
@tanstack/solid-query 77.81% <ø> (ø)
@tanstack/solid-query-devtools 64.17% <ø> (ø)
@tanstack/solid-query-persist-client 100.00% <ø> (ø)
@tanstack/svelte-query ∅ <ø> (∅)
@tanstack/svelte-query-devtools ∅ <ø> (∅)
@tanstack/svelte-query-persist-client ∅ <ø> (∅)
@tanstack/vue-query 71.91% <ø> (ø)
@tanstack/vue-query-devtools ∅ <ø> (∅)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sukvvon sukvvon force-pushed the fix/hydration-boundary-double-fetch branch from cfa5d74 to 695fa28 Compare December 27, 2025 17:38
@sukvvon sukvvon marked this pull request as ready for review December 29, 2025 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation package: query-core package: react-query

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HydrationBoundary double fetching on subsequent visits

1 participant