fix(hydration): set dataUpdatedAt when pending query resolves before hydration#10610
fix(hydration): set dataUpdatedAt when pending query resolves before hydration#10610DORI2001 wants to merge 1 commit intoTanStack:mainfrom
Conversation
📝 WalkthroughWalkthroughThe change fixes hydration of queries that were pending during dehydration but resolved with data before hydration occurs. It now correctly sets Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/query-core/src/hydration.ts (1)
233-257:⚠️ Potential issue | 🟠 MajorOlder payloads can still skip pending→success upgrade in existing-query hydration.
When
dehydratedAtis missing (older payload),hasNewerSyncDatais always false (Line 237), andstate.dataUpdatedAtis typically0for pending dehydrated state. That can keep theifon Line 239 false, so the transition at Line 253 and the timestamp fix at Line 256 never run for existing pending queries.Suggested fix
- const hasNewerSyncData = - syncData && - // We only need this undefined check to handle older dehydration - // payloads that might not have dehydratedAt - dehydratedAt !== undefined && - dehydratedAt > query.state.dataUpdatedAt + const hasNewerSyncData = + syncData && + (dehydratedAt !== undefined + ? dehydratedAt > query.state.dataUpdatedAt + : // Older payloads: allow only pending->success upgrade for existing pending-without-data + state.status === 'pending' && + query.state.status === 'pending' && + query.state.data === undefined)🤖 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 233 - 257, The hydration logic currently skips the pending→success upgrade when dehydratedAt is missing; update the hasNewerSyncData computation to treat an undefined dehydratedAt as “newer” for pending dehydrated states (e.g., set hasNewerSyncData = syncData && (dehydratedAt !== undefined ? dehydratedAt > query.state.dataUpdatedAt : state.status === 'pending')). Then ensure when you call query.setState (same block around query.setState and the pending→success branch) you still use dataUpdatedAt: dehydratedAt ?? Date.now() so the timestamp correction runs even if dehydratedAt was missing. This references the variables dehydratedAt, syncData, state.status, state.dataUpdatedAt and the call to query.setState.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@packages/query-core/src/hydration.ts`:
- Around line 233-257: The hydration logic currently skips the pending→success
upgrade when dehydratedAt is missing; update the hasNewerSyncData computation to
treat an undefined dehydratedAt as “newer” for pending dehydrated states (e.g.,
set hasNewerSyncData = syncData && (dehydratedAt !== undefined ? dehydratedAt >
query.state.dataUpdatedAt : state.status === 'pending')). Then ensure when you
call query.setState (same block around query.setState and the pending→success
branch) you still use dataUpdatedAt: dehydratedAt ?? Date.now() so the timestamp
correction runs even if dehydratedAt was missing. This references the variables
dehydratedAt, syncData, state.status, state.dataUpdatedAt and the call to
query.setState.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 16c83652-7389-4dc8-9f6d-243d04125a88
📒 Files selected for processing (1)
packages/query-core/src/hydration.ts
Problem
When a query is dehydrated while still pending (streamed) but then resolves before hydration runs on the client, the hydration code transitions the query's status from
'pending'to'success'— but it never updatesdataUpdatedAt. It stays at0.This was introduced in #10444, which removed the
query.fetch()call that previously setdataUpdatedAtas a side effect of resolving the promise.Steps to reproduce: dehydrate a pending query (e.g. from a React Server Component), resolve it before sending the response, then hydrate on the client.
query.state.dataUpdatedAtwill be0instead of a real timestamp.Fix
When the pending→success transition is applied (both in the
setStatepath for existing queries and in thequeryCache.buildpath for new queries), adddataUpdatedAt: dehydratedAt ?? Date.now().dehydratedAtis the right value here — it records when the server serialized the resolved data, which is when the data was actually "updated". Falling back toDate.now()handles older dehydration payloads that don't includedehydratedAt.Fixes #10603
Summary by CodeRabbit