Skip to content

fix(solid-query): untrack the one-shot client and options reads on mount - #11456

Draft
mnkprs wants to merge 3 commits into
TanStack:mainfrom
mnkprs:fix/solid-query-untracked-initial-reads
Draft

fix(solid-query): untrack the one-shot client and options reads on mount#11456
mnkprs wants to merge 3 commits into
TanStack:mainfrom
mnkprs:fix/solid-query-untracked-initial-reads

Conversation

@mnkprs

@mnkprs mnkprs commented Sep 9, 2026

Copy link
Copy Markdown

Fixes #11358.

Every solid-query hook seeds an observer or a signal from client() and its options accessor while the hook itself is running. Later changes reach those observers through setOptions/setQueries and the computations around them, so those initial reads are one-shot by design — but they were still made inside whatever tracking scope called the hook. On Solid 2 that makes each hook call log a [STRICT_READ_UNTRACKED] diagnostic on mount, so a screen with a handful of queries and mutations fills the console with library noise and drowns out the diagnostics that are about application code.

This wraps those reads in untrack, which is the case it exists for. Affected:

  • useBaseQueryinitialOptions, the Observer construction, and the first getOptimisticResult
  • useMutation — the MutationObserver construction
  • useQueries — the QueriesObserver, the initial store contents, the seeding batch, and the initial getProxies()
  • useIsFetching / useIsMutating / useMutationState — the signal seeds

Why this is not a behaviour change for components

Solid's createComponent already wraps component bodies in untrack, so for the way these hooks are actually called the reads were untracked before this PR too. I checked this by running the same scenarios against the merge base and against this branch — client swaps and option changes behave identically in a component either way.

The behaviour that does change is a hook called directly inside a createComputed/createMemo. There, the tracked reads used to tear the hook down and rebuild it wholesale whenever the client or the options changed. That accidental rebuild is what the new tests pin down as gone.

Tests

src/__tests__/untrackedReads.test.tsx calls each hook inside a computation that reads nothing itself, then invalidates the client and the options the hook read, and asserts the computation does not re-run. This makes the leak observable on Solid 1 as well, rather than only as a Solid 2 diagnostic. All six fail on main (3 runs instead of 1) and pass here.

Note for reviewers

While verifying the above I found three pre-existing bugs that this PR deliberately does not touch, since they are out of scope for the ticket and all reproduce on main today in ordinary component usage:

  1. useBaseQuery's on(client, …) handler calls createClientSubscriber() — which reads the observer signal — before setObserver(newObserver), so it resubscribes to the outgoing observer and later updates to the new client's cache never arrive.
  2. useMutation and useQueries have no client-change handling at all; swapping the client leaves them on the old one.
  3. useMutationState never recomputes when its filters change — the solid twin of useMutationState does not update when filters change (react-query) #11272.

I have fixes and regression tests ready for (1) and (3) and will send them as separate PRs if you'd like them.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested code changes locally with pnpm run test:pr, or these tests do not apply to this pull request.
  • I fully understand the code in this pull request, including any code generated with AI assistance.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 7e29fd20-56e2-4834-adcc-ecffd8e3f986

📥 Commits

Reviewing files that changed from the base of the PR and between f7c6be3 and d850a22.

📒 Files selected for processing (8)
  • .changeset/solid-query-untracked-initial-reads.md
  • packages/solid-query/src/__tests__/untrackedReads.test.tsx
  • packages/solid-query/src/useBaseQuery.ts
  • packages/solid-query/src/useIsFetching.ts
  • packages/solid-query/src/useIsMutating.ts
  • packages/solid-query/src/useMutation.ts
  • packages/solid-query/src/useMutationState.ts
  • packages/solid-query/src/useQueries.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

Solid Query now wraps one-shot initialization reads in untrack() across query, mutation, fetching, and mutation-state hooks. New tests verify that these reads do not create reactive dependencies. A patch changeset documents the update.

Changes

Solid Query untracked initialization

Layer / File(s) Summary
Observer initialization
packages/solid-query/src/useBaseQuery.ts, packages/solid-query/src/useMutation.ts, packages/solid-query/src/useQueries.ts
Initial observer construction, option reads, state seeding, batch updates, and proxy creation now use untrack().
Status and mutation-state initialization
packages/solid-query/src/useIsFetching.ts, packages/solid-query/src/useIsMutating.ts, packages/solid-query/src/useMutationState.ts
Initial fetching, mutating, and mutation-state signal values now use untracked reads.
Validation and release metadata
packages/solid-query/src/__tests__/untrackedReads.test.tsx, .changeset/solid-query-untracked-initial-reads.md
Tests verify that six hooks do not rerun after their client or key signals change. The changeset records a patch release.

Priority: ➖ Normal

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

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to d850a

Solid Query hook initialization no longer creates unintended reactive dependencies or Solid 2 diagnostics, while normal observer-driven updates remain intact. The affected hook behaviors are covered by focused tests and no merge-blocking risk remains.

Suggested reviewers: sukvvon

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy issue #11358 by wrapping intentional one-time client and options reads in untrack. The implementation covers useBaseQuery and useMutation from the issue, plus related Solid Query h…
Out of Scope Changes check ✅ Passed The code changes support the linked issue objective and the stated untracked-read behavior. The additional affected hooks and regression tests are directly related. The description clearly identifies …
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 7 files. (1 skipped: 1 …
Description check ✅ Passed The description clearly explains the motivation, affected hooks, behavior, tests, scope, and release impact. It includes all required template sections and completed checklist items.
Title check ✅ Passed The title is concise, specific, and accurately summarizes the primary change: untracking one-shot client and options reads in solid-query hooks.
✨ 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.

Every hook seeds an observer or a signal from `client()` and its options
accessor while the hook itself is running. Later changes reach the observers
through `setOptions`/`setQueries` and the surrounding computations, so those
initial reads are one-shot by design — but they were still made inside
whatever tracking scope called the hook.

On Solid 2 that makes each hook call log a `[STRICT_READ_UNTRACKED]`
diagnostic on mount, drowning out the diagnostics that are about application
code. It is also observable on Solid 1: a hook called from inside a
computation makes that computation depend on the client and the options, so
changing either re-runs it and rebuilds the observer.

Wrap the reads in `untrack`, which is exactly the case it exists for.

Fixes TanStack#11358
@mnkprs
mnkprs force-pushed the fix/solid-query-untracked-initial-reads branch from d850a22 to 4cd21ee Compare September 9, 2026 11:57
@mnkprs
mnkprs marked this pull request as draft September 9, 2026 11:57
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.

solid-query: STRICT_READ_UNTRACKED on Solid 2 — client()/options() read in component body (useMutation, useBaseQuery)

1 participant