Skip to content

fix(react-query): hydrate deferred queries before observers subscribe to avoid a redundant refetch - #11137

Open
ostapondo wants to merge 1 commit into
TanStack:mainfrom
ostapondo:fix/hydration-boundary-deferred-refetch
Open

fix(react-query): hydrate deferred queries before observers subscribe to avoid a redundant refetch#11137
ostapondo wants to merge 1 commit into
TanStack:mainfrom
ostapondo:fix/hydration-boundary-deferred-refetch

Conversation

@ostapondo

@ostapondo ostapondo commented Aug 3, 2026

Copy link
Copy Markdown

🎯 Changes

Fixes #9610.

HydrationBoundary defers hydration of queries that already exist in the cache to an effect, so that transitions don't update existing observers mid-render. But useQuery's useSyncExternalStore subscription is also a passive effect, and children's effects run before parents'. On a revisit (query cached but unmounted), the observer subscribes first, sees the old stale entry, and starts a refetch of the exact data the dehydrated state already contains — the server computes it in the loader and the client immediately fetches it again.

This moves the deferred hydration (and the optionsRef sync it reads) to a layout effect. All layout effects run before any passive effects, so hydration lands in the cache before any remounting observer decides whether to fetch. Layout effects still only run when the tree commits, so the aborted-transition behaviour is unchanged (the existing test for it stays green).

Behaviour change worth noting: already-mounted observers now receive hydrated data at commit time rather than after paint. One timing assertion in an existing test was updated accordingly; the render-phase purity guarantee is still covered by the aborted-transition test.

Not covered: a useSuspenseQuery over a query that is in pending state with no data starts its fetch during render, before any layout effect — that pre-existing path is unaffected by this change.

Verified with a test that fails on main (refetch fires, stale data rendered) and passes with the fix, plus a jsdom repro of the React Router loader flow from the issue against the built package.

✅ 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

    • Improved hydration timing so deferred query data is available immediately to components.
    • Prevented remounted queries from unnecessarily refetching data already available from hydration.
    • Preserved reliable behavior during interrupted transitions.
  • Tests

    • Added coverage for hydrated data in existing, new, and inactive queries.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: be7b25d4-473e-47f2-a775-20b3daab4ee8

📥 Commits

Reviewing files that changed from the base of the PR and between 46d7f02 and 69c3b50.

📒 Files selected for processing (3)
  • .changeset/hungry-planes-tease.md
  • packages/react-query/src/HydrationBoundary.tsx
  • packages/react-query/src/__tests__/HydrationBoundary.test.tsx

📝 Walkthrough

Walkthrough

HydrationBoundary now performs deferred query hydration in an isomorphic layout effect in browsers. Tests verify immediate hydrated data and prevent redundant refetches when queries remount.

Changes

HydrationBoundary hydration timing

Layer / File(s) Summary
Isomorphic deferred hydration
packages/react-query/src/HydrationBoundary.tsx
The boundary selects useEffect on the server and useLayoutEffect in the browser. Option synchronization and queued hydration use this hook.
Hydration remount validation
packages/react-query/src/__tests__/HydrationBoundary.test.tsx, .changeset/hungry-planes-tease.md
Tests expect immediate hydrated data and verify that remounting an inactive query does not call its query function again. A patch changeset documents the fix.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes moving hydration before observer subscriptions to prevent redundant refetches.
Description check ✅ Passed The description covers the change, motivation, testing, checklist, and release impact with all required sections completed.
Linked Issues check ✅ Passed The implementation and tests address issue #9610 by preventing redundant refetches after deferred hydration on subsequent visits.
Out of Scope Changes check ✅ Passed The changeset, implementation, and regression tests are directly related to the linked issue and stated pull request objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HydrationBoundary double fetching on subsequent visits

1 participant