Skip to content

Conversation

@976520
Copy link
Contributor

@976520 976520 commented Jan 29, 2026

🎯 Changes

Fixes #9978

Recreate the query promise when a retry transitions the query to fetching, ensuring useQuery().promise provides a fresh pending promise to Suspense.

Explicitly refetch errored queries on QueryErrorResetBoundary reset, even when useQuery is outside the ErrorBoundary.

Limit refetching to active and enabled queries to avoid unintended retries.

✅ 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

  • New Features

    • Error reset boundary can track specific queries and will refetch registered failed queries on reset; new tracking helpers exported for registering/query identification.
    • Added a promise-hash typing export and a small utility export to the core query module.
  • Bug Fixes

    • More consistent handling of pending and rejected query promises so retries, optimistic results, and fetch-status transitions behave more reliably after errors.
  • Tests

    • Added tests covering promise-based query retries, scoped registry behavior, StrictMode interactions, and tracking queries outside boundaries.

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

@changeset-bot
Copy link

changeset-bot bot commented Jan 29, 2026

⚠️ No Changeset found

Latest commit: d4cd23a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 29, 2026

📝 Walkthrough

Walkthrough

Tags thenables with a queryHash in QueryObserver, exposes PromiseWithHash, registers/tracks query hashes in QueryErrorResetBoundary to refetch registered errored queries via QueryClient on reset, adds getQueryHash/useTrackQueryHash helpers, registers queries in useBaseQuery for certain render modes, and adds tests for scoped registry and retry behavior.

Changes

Cohort / File(s) Summary
QueryObserver / promise tagging
packages/query-core/src/queryObserver.ts
Attach queryHash to thenables via new tagThenable(...); export PromiseWithHash<T>; use tagged thenables for current/pending promises; recreate pending thenable for certain rejected→fetching transitions.
Query-core exports
packages/query-core/src/index.ts
Re-export resolveEnabled from ./utils.
QueryErrorResetBoundary & tracking
packages/react-query/src/QueryErrorResetBoundary.tsx, packages/react-query/src/index.ts
Add register to boundary value, implement getQueryHash and useTrackQueryHash, override reset to refetch queries with registered hashes via useQueryClient, and re-export helpers.
useBaseQuery change
packages/react-query/src/useBaseQuery.ts
Call errorResetBoundary.register(queryHash) when experimental_prefetchInRender or suspense is enabled before cache entry creation.
Tests — react-query
packages/react-query/src/__tests__/useQuery.promise.test.tsx, packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx
Add tests covering QERB-triggered retries with useQuery().promise, scoped registry behavior, StrictMode double registration, and useTrackQueryHash integration (multiple new test cases).

Sequence Diagram(s)

sequenceDiagram
  participant Component
  participant Observer as QueryObserver
  participant Server
  participant QERB as QueryErrorResetBoundary
  participant Client as QueryClient

  Component->>Observer: read tagged promise (useQuery().promise)
  Observer->>Server: fetch -> error
  Observer-->>Component: throws (ErrorBoundary)
  Component->>QERB: reset triggered (via ErrorBoundary.onReset)
  QERB->>Client: refetchQueries(predicate matching registered hashes)
  Client->>Observer: request refetch for matching queries
  Observer->>Server: fetch -> success
  Observer-->>Component: resolve tagged promise with data
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • TkDodo

Poem

🐰
I tag the thread where promises play,
A little hash to mark the way.
Boundaries ring, the client hops,
Retries bloom — no more stops.
I nibble bugs and dance all day.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% 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 clearly and concisely describes the main change: fixing retry behavior of useQuery().promise when an error boundary reset occurs.
Description check ✅ Passed The PR description covers key changes, references the linked issue (#9978), follows the repository template with all sections completed, and includes checklist items marked as done.
Linked Issues check ✅ Passed The code changes directly address issue #9978 by implementing all stated objectives: recreating pending promise when retrying, explicitly refetching errored queries on reset, and limiting refetching to active/enabled queries.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the useQuery().promise retry issue with QueryErrorResetBoundary. No unrelated modifications were introduced.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 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.

@nx-cloud
Copy link

nx-cloud bot commented Jan 29, 2026

View your CI Pipeline Execution ↗ for commit d4cd23a

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

☁️ Nx Cloud last updated this comment at 2026-01-31 06:25:53 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 29, 2026

More templates

@tanstack/angular-query-experimental

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

@tanstack/eslint-plugin-query

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

@tanstack/query-async-storage-persister

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

@tanstack/query-broadcast-client-experimental

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

@tanstack/query-core

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

@tanstack/query-devtools

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

@tanstack/query-persist-client-core

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

@tanstack/query-sync-storage-persister

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

@tanstack/react-query

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

@tanstack/react-query-devtools

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

@tanstack/react-query-next-experimental

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

@tanstack/react-query-persist-client

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

@tanstack/solid-query

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

@tanstack/solid-query-devtools

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

@tanstack/solid-query-persist-client

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

@tanstack/svelte-query

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

@tanstack/svelte-query-devtools

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

@tanstack/svelte-query-persist-client

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

@tanstack/vue-query

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

@tanstack/vue-query-devtools

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

commit: d4cd23a

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

🤖 Fix all issues with AI agents
In `@packages/react-query/src/QueryErrorResetBoundary.tsx`:
- Around line 26-33: The predicate used in client?.refetchQueries incorrectly
treats observer.options.enabled functions as truthy; update the predicate to
resolve the enabled state using resolveEnabled from query-core (same approach as
in query.ts:270) so the check becomes something like resolving
observer.options.enabled against the query and comparing to !== false before
counting it as enabled; modify the predicate passed to refetchQueries where
query, observer, and observer.options.enabled are referenced and import/use
resolveEnabled to properly evaluate callback-based enabled options.
🧹 Nitpick comments (1)
packages/react-query/src/QueryErrorResetBoundary.tsx (1)

61-62: Consider potential stale closure with QueryClient.

The useState initializer captures the client reference at mount time. While the QueryClient instance is typically stable throughout an app's lifecycle, if it were to change (e.g., during testing or in edge cases), the value.reset() would continue using the original client.

This is likely acceptable given typical usage patterns, but worth noting for awareness.

@sentry
Copy link

sentry bot commented Jan 29, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 42.74%. Comparing base (ed5e5fc) to head (00af302).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main   #10079      +/-   ##
==========================================
+ Coverage   36.87%   42.74%   +5.87%     
==========================================
  Files         245      172      -73     
  Lines       11103     8267    -2836     
  Branches     2083     1687     -396     
==========================================
- Hits         4094     3534     -560     
+ Misses       6484     4343    -2141     
+ Partials      525      390     -135     
Components Coverage Δ
@tanstack/angular-query-experimental 46.35% <ø> (ø)
@tanstack/eslint-plugin-query ∅ <ø> (∅)
@tanstack/query-async-storage-persister 43.85% <ø> (ø)
@tanstack/query-broadcast-client-experimental 24.39% <ø> (ø)
@tanstack/query-codemods ∅ <ø> (∅)
@tanstack/query-core 75.58% <100.00%> (+0.03%) ⬆️
@tanstack/query-devtools 3.38% <ø> (ø)
@tanstack/query-persist-client-core 80.12% <ø> (ø)
@tanstack/query-sync-storage-persister 84.61% <ø> (ø)
@tanstack/query-test-utils ∅ <ø> (∅)
@tanstack/react-query 29.38% <100.00%> (+0.38%) ⬆️
@tanstack/react-query-devtools 9.25% <ø> (ø)
@tanstack/react-query-next-experimental ∅ <ø> (∅)
@tanstack/react-query-persist-client 100.00% <ø> (ø)
@tanstack/solid-query 53.52% <ø> (ø)
@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 35.67% <ø> (ø)
@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.

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

🤖 Fix all issues with AI agents
In `@packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx`:
- Around line 1063-1068: TrackedChild currently passes a raw string to
useTrackQueryHash (via queryClient.getQueryCache().build(...).queryHash), but
getQueryHash only registers objects; update TrackedChild to pass an object
instead (for example call useTrackQueryHash({ queryHash: hash }) or pass the
original query descriptor like useTrackQueryHash({ queryKey: key })) so the hook
registers the tracked query; change the call site in TrackedChild where
useTrackQueryHash(...) is invoked.

…t.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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

🤖 Fix all issues with AI agents
In `@packages/react-query/src/__tests__/QueryResetErrorBoundary.test.tsx`:
- Around line 1063-1072: There is a stray closing brace after the TrackedChild
component that breaks parsing; remove the extra `}` following the TrackedChild
function declaration so the component's braces match correctly (locate the
TrackedChild function that calls queryClient.getQueryCache().build(...) and
useTrackQueryHash and delete the superfluous closing brace to restore valid
syntax).

@976520
Copy link
Contributor Author

976520 commented Jan 31, 2026

@TkDodo

after reading your feedback, I had a new idea and reworked the approach. I've updated the logic to use a registry-based tracking system with queryHash-tagged promises.

Could you please take another look ?

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.

experimental_prefetchInRender does not work with useQueryErrorResetBoundary

2 participants