Skip to content

fix(solid-query): don't trigger Suspense when data is already cached#10592

Open
ousamabenyounes wants to merge 1 commit intoTanStack:mainfrom
ousamabenyounes:fix/issue-9955
Open

fix(solid-query): don't trigger Suspense when data is already cached#10592
ousamabenyounes wants to merge 1 commit intoTanStack:mainfrom
ousamabenyounes:fix/issue-9955

Conversation

@ousamabenyounes
Copy link
Copy Markdown
Contributor

@ousamabenyounes ousamabenyounes commented Apr 26, 2026

Summary

Fixes #9955.

When data was preloaded via ensureQueryData (e.g. from a TanStack Router loader), accessing state.data in a Suspense boundary would re-trigger the Suspense fallback even though the data was already in cache and isSuccess was true.

Root cause

In useBaseQuery.ts, the proxy data getter reads queryResource.latest?.data whenever state.data !== undefined. Per Solid's docs, .latest falls back to a read()-equivalent (and therefore triggers Suspense) when the resource has no prior resolved value.

In the preloaded-cache case, the resource's fetcher synchronously calls resolve(...) because !observerResult.isLoading, but the resource doesn't transition out of the initial pending state until the next microtask. Reading .latest inside that microtask gap suspends the component even though state.data already holds the cached value.

Fix

If the store already has data and no fetch is in-flight (!state.isFetching), return state.data directly without going through queryResource. state.isFetching is read via untrack so it doesn't widen the data subscriber's reactive deps (preserves existing reconcile / memo-churn tests).

When a fetch is in-flight, the existing queryResource.latest?.data path is preserved so background-refresh and re-mount semantics are unchanged.

Verification

  • Added regression test in packages/solid-query/src/__tests__/suspense.test.tsx that pre-populates the cache via queryClient.ensureQueryData(...), renders inside <Suspense>, and asserts the fallback never mounts. Test fails on main (Suspense fires), passes with the fix.
  • pnpm exec nx run @tanstack/solid-query:test:lib — all 310 tests pass.
  • pnpm exec nx run @tanstack/solid-query:test:types — all TS versions pass.
  • pnpm exec nx run @tanstack/solid-query:test:eslint — clean.
  • Changeset added (patch for @tanstack/solid-query).

Test plan

🤖 Generated with Claude Code
Generated by Ora Studio
Vibe coded by ousamabenyounes

Summary by CodeRabbit

  • Bug Fixes

    • Prevented unnecessary Suspense activation when query data is already available in cache, including cases where data was preloaded via ensureQueryData.
  • Tests

    • Added tests confirming Suspense fallbacks do not mount when query data is cached or preloaded, ensuring components render immediately and avoid unnecessary loading states.

Generated by Ora Studio (with Claude Code)
Vibe coded by Ben Younes Ousama

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 26, 2026

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

Run ID: e1fbe08d-b1ef-4a2c-a8f9-91520bdcc193

📥 Commits

Reviewing files that changed from the base of the PR and between fe12586 and c947290.

📒 Files selected for processing (3)
  • .changeset/solid-query-suspense-ensure-data.md
  • packages/solid-query/src/__tests__/suspense.test.tsx
  • packages/solid-query/src/useBaseQuery.ts
✅ Files skipped from review due to trivial changes (1)
  • .changeset/solid-query-suspense-ensure-data.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/solid-query/src/tests/suspense.test.tsx
  • packages/solid-query/src/useBaseQuery.ts

📝 Walkthrough

Walkthrough

This PR prevents unnecessary Solid Suspense activation when query data is already cached (including ensureQueryData preloads) by changing the data getter to return cached state.data only when no fetch is in-flight, adding a test, and adding a changeset note.

Changes

Solid Query Suspense Fix

Layer / File(s) Summary
Imports
packages/solid-query/src/useBaseQuery.ts
Adds untrack to Solid imports to allow conditional dependency checks.
Core Getter Change
packages/solid-query/src/useBaseQuery.ts
prop === 'data' now returns state.data only when untrack(() => state.isFetching) is falsy; otherwise it reads from the Solid resource (queryResource.latest?.data / queryResource()?.data).
Tests
packages/solid-query/src/__tests__/suspense.test.tsx
New test preloads data via queryClient.ensureQueryData (with staleTime: Infinity) and asserts that mounting useQuery inside Suspense renders the preloaded value without mounting the fallback.
Changeset Documentation
.changeset/solid-query-suspense-ensure-data.md
Patch release note describing the fix to avoid triggering Suspense when data is already available in cache, including ensureQueryData scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I peeked at state without a fright,
Preloaded data shining bright,
With a little untrack,
The suspense stayed back,
Now renders hop on through the night ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 'fix(solid-query): don't trigger Suspense when data is already cached' clearly summarizes the main fix being addressed—preventing unnecessary Suspense triggering when cached data is available.
Description check ✅ Passed The description comprehensively covers the issue, root cause, fix details, and verification steps with clear alignment to the provided template structure.
Linked Issues check ✅ Passed All code changes directly address the objectives from issue #9955: the useBaseQuery proxy getter now short-circuits when data is cached and no fetch is in-flight, preventing spurious Suspense triggers.
Out of Scope Changes check ✅ Passed All three changes (changeset note, regression test, and useBaseQuery fix) are directly scoped to resolving the Suspense caching issue without introducing unrelated modifications.

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

✨ 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 and usage tips.

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Apr 26, 2026

View your CI Pipeline Execution ↗ for commit c947290

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

☁️ Nx Cloud last updated this comment at 2026-05-07 08:53:08 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 26, 2026

More templates

@tanstack/angular-query-experimental

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

@tanstack/eslint-plugin-query

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

@tanstack/preact-query

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

@tanstack/preact-query-devtools

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

@tanstack/preact-query-persist-client

npm i https://pkg.pr.new/@tanstack/preact-query-persist-client@10592

@tanstack/query-async-storage-persister

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

@tanstack/query-broadcast-client-experimental

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

@tanstack/query-core

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

@tanstack/query-devtools

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

@tanstack/query-persist-client-core

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

@tanstack/query-sync-storage-persister

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

@tanstack/react-query

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

@tanstack/react-query-devtools

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

@tanstack/react-query-next-experimental

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

@tanstack/react-query-persist-client

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

@tanstack/solid-query

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

@tanstack/solid-query-devtools

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

@tanstack/solid-query-persist-client

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

@tanstack/svelte-query

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

@tanstack/svelte-query-devtools

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

@tanstack/svelte-query-persist-client

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

@tanstack/vue-query

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

@tanstack/vue-query-devtools

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

commit: c947290

When data was preloaded via `ensureQueryData` (e.g. from a router loader),
the proxy `data` getter read `queryResource.latest`, which falls back to a
suspending read while the resource is in its initial pending state, even
though the fetcher had already synchronously resolved with cached data.

If the store already has data and no fetch is in-flight, return `state.data`
directly. `state.isFetching` is read via `untrack` so it doesn't widen the
data subscriber's reactive deps.

Fixes TanStack#9955
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.

Solid Suspense is triggered on data access after checking isSuccess when loaded by ensureQueryData

1 participant