close
Skip to content

fix(solid-query): resolve query client outside memos#10571

Open
KimHyeongRae0 wants to merge 3 commits intoTanStack:mainfrom
KimHyeongRae0:fix/10445-solid-query-client-resolver
Open

fix(solid-query): resolve query client outside memos#10571
KimHyeongRae0 wants to merge 3 commits intoTanStack:mainfrom
KimHyeongRae0:fix/10445-solid-query-client-resolver

Conversation

@KimHyeongRae0
Copy link
Copy Markdown
Contributor

@KimHyeongRae0 KimHyeongRae0 commented Apr 25, 2026

Changes

Closes #10445.

Solid hooks no longer call useQueryClient from inside createMemo. useQueryClientResolver captures QueryClientContext at hook top level, then affected hooks call the returned accessor from memos.

This keeps explicit queryClient accessors reactive and leaves the existing useQueryClient(client) API unchanged.

Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

Local verification:

  • pnpm --filter @tanstack/solid-query test:lib
  • pnpm --filter @tanstack/solid-query test:types:tscurrent
  • pnpm --filter @tanstack/solid-query test:eslint

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

    • Safely resolve the query client inside reactive callbacks and ensure hooks resubscribe correctly when a custom client signal changes.
  • Tests

    • Added tests verifying resolver behavior inside reactive computations and resubscription when a custom client changes.
  • Chores

    • Released a patch version update for the Solid Query package.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 762cd389-4c48-49b3-b03e-595d7300e0ad

📥 Commits

Reviewing files that changed from the base of the PR and between 0843b31 and 3970ffc.

📒 Files selected for processing (2)
  • packages/solid-query/src/__tests__/useIsFetching.test.tsx
  • packages/solid-query/src/__tests__/useIsMutating.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/solid-query/src/tests/useIsMutating.test.tsx

📝 Walkthrough

Walkthrough

Adds a top-level QueryClient resolver (useQueryClientResolver) and updates hooks/tests so the QueryClient context is read at hook top level and the returned accessor can be safely called inside reactive memos; also moves some cache subscription wiring into effects to handle client changes.

Changes

Query Client Context Resolution Pattern

Layer / File(s) Summary
Resolver Core
packages/solid-query/src/QueryClientProvider.tsx
Adds queryClientContextError constant, exports useQueryClientResolver(queryClient?: Accessor<QueryClient | undefined>): Accessor<QueryClient>, and updates useQueryClient to throw the shared error when no client is available.
Hook Migrations — client resolution
packages/solid-query/src/useBaseQuery.ts, packages/solid-query/src/useQueries.ts, packages/solid-query/src/useMutation.ts, packages/solid-query/src/useMutationState.ts
Replace createMemo(() => useQueryClient(queryClient?.())) with const resolveClient = useQueryClientResolver(queryClient) and createMemo(() => resolveClient()), moving context resolution out of memo callbacks.
Hook Migrations — cache subscription wiring
packages/solid-query/src/useIsFetching.ts, packages/solid-query/src/useIsMutating.ts
Resolve client via useQueryClientResolver(queryClient); move subscription lifecycle into createEffect so subscriptions update and clean up when the resolved client or caches change.
Tests
packages/solid-query/src/__tests__/QueryClientProvider.test.tsx, packages/solid-query/src/__tests__/useIsFetching.test.tsx, packages/solid-query/src/__tests__/useIsMutating.test.tsx
Add tests confirming the resolver can be called inside Solid reactive computations, that missing-provider errors are deferred until resolver invocation, and that useIsFetching/useIsMutating resubscribe when a reactive custom queryClient changes.
Changelog / Changeset
.changeset/solid-query-client-resolver.md
Adds a patch changeset documenting that query client context is resolved outside reactive memo callbacks.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰
I nudged the context forward, steady and light,
So memos can call without a surprise fright.
Subscriptions now follow when clients rearrange,
Tests clap their paws — the resolver's in range.
A hop, a patch, and everything's right.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% 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 PR title clearly and specifically summarizes the main change: introducing a resolver to handle query client resolution outside of memos in solid-query.
Description check ✅ Passed The PR description covers the key changes (introduces useQueryClientResolver, affects multiple hooks, closes #10445), includes the checklist template with items appropriately checked, and documents local verification steps.
Linked Issues check ✅ Passed The PR successfully addresses all coding requirements from issue #10445: introduces useQueryClientResolver to capture context at hook top level, replaces all createMemo(() => useQueryClient(...)) patterns with resolver-based approach, and adds tests validating client subscription cleanup and context resolution behavior.
Out of Scope Changes check ✅ Passed All changes align with the scope of issue #10445: adding useQueryClientResolver, updating affected hooks (useBaseQuery, useQueries, useMutation, useIsFetching, useIsMutating, useMutationState), creating a changeset, and adding tests for subscription cleanup behavior.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Apr 25, 2026

View your CI Pipeline Execution ↗ for commit 3970ffc

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

☁️ Nx Cloud last updated this comment at 2026-05-03 14:05:35 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 25, 2026

More templates

@tanstack/angular-query-experimental

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

@tanstack/eslint-plugin-query

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

@tanstack/preact-query

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

@tanstack/preact-query-devtools

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

@tanstack/preact-query-persist-client

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

@tanstack/query-async-storage-persister

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

@tanstack/query-broadcast-client-experimental

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

@tanstack/query-core

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

@tanstack/query-devtools

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

@tanstack/query-persist-client-core

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

@tanstack/query-sync-storage-persister

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

@tanstack/react-query

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

@tanstack/react-query-devtools

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

@tanstack/react-query-next-experimental

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

@tanstack/react-query-persist-client

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

@tanstack/solid-query

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

@tanstack/solid-query-devtools

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

@tanstack/solid-query-persist-client

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

@tanstack/svelte-query

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

@tanstack/svelte-query-devtools

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

@tanstack/svelte-query-persist-client

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

@tanstack/vue-query

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

@tanstack/vue-query-devtools

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

commit: 3970ffc

@KimHyeongRae0 KimHyeongRae0 marked this pull request as ready for review May 2, 2026 12:50
Copy link
Copy Markdown
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.

🧹 Nitpick comments (4)
packages/solid-query/src/useIsMutating.ts (1)

11-23: ⚡ Quick win

Same subscription-reactivity gap as useIsFetching — wrap in createEffect.

mutationCache().subscribe(...) at line 19 is called once at initialization. If client changes, the subscription won't follow the new mutation cache. Apply the same createEffect-based pattern used in useMutationState:

♻️ Suggested fix
-  const [mutations, setMutations] = createSignal(
-    client().isMutating(filters?.()),
-  )
-
-  const unsubscribe = mutationCache().subscribe((_result) => {
-    setMutations(client().isMutating(filters?.()))
-  })
-
-  onCleanup(unsubscribe)
+  const [mutations, setMutations] = createSignal(0)
+
+  createEffect(() => {
+    setMutations(client().isMutating(filters?.()))
+    const unsubscribe = mutationCache().subscribe(() => {
+      setMutations(client().isMutating(filters?.()))
+    })
+    onCleanup(unsubscribe)
+  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-query/src/useIsMutating.ts` around lines 11 - 23, The
subscription to mutationCache in useIsMutating is created once and won't track
changes if the resolved client changes; wrap the subscription logic in a
createEffect (like useMutationState) that reads resolveClient()/client() and
mutationCache() so it re-subscribes when client changes, call
setMutations(client().isMutating(filters?.())) inside the effect, and ensure you
call onCleanup to unsubscribe the previous mutationCache.subscribe; update
functions/variables involved: resolveClient, client, mutationCache,
setMutations, createEffect, and onCleanup so the subscription follows client
changes.
packages/solid-query/src/useMutation.ts (1)

24-25: ⚖️ Poor tradeoff

client memo is only read at initialization; client changes won't be reflected in the observer.

client (line 25) is read once at line 32 to construct MutationObserver. There is no reactive listener (e.g. createComputed(on(client, ...))) that would recreate the observer if the resolved QueryClient changes later. This is pre-existing behavior, but unlike useBaseQuery which handles client-swap via on(client, ...), useMutation silently continues using the original client.

Worth tracking in a follow-up if dynamic client swapping needs to be supported.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-query/src/useMutation.ts` around lines 24 - 25, The
MutationObserver in useMutation is constructed once using the memoized client
(client = createMemo(() => resolveClient())) and never updated if the resolved
QueryClient changes; modify useMutation so it watches the client memo and
recreates/updates the MutationObserver when client changes (similar to
useBaseQuery's on(client, ...) pattern): use a reactive listener (e.g.,
createComputed(on(client, ...))) inside useMutation to dispose the old
MutationObserver and instantiate a new MutationObserver bound to the new
resolveClient() result so the observer always uses the current QueryClient.
packages/solid-query/src/useIsFetching.ts (1)

11-21: ⚡ Quick win

Subscription doesn't react to client changes — inconsistent with useMutationState.

The queryCache().subscribe(...) call at line 17 is established once, outside any reactive scope. If client changes (e.g. the provider swaps its QueryClient), queryCache recomputes but the subscription stays bound to the old cache. useMutationState avoids this correctly by wrapping its subscription in createEffect:

createEffect(() => {
  const unsubscribe = mutationCache().subscribe(...)
  onCleanup(unsubscribe)
})

This PR makes client correctly reactive for the first time (fixing the useContext-in-memo bug), so this latent subscription gap is now more likely to surface.

♻️ Suggested fix
-  const [fetches, setFetches] = createSignal(client().isFetching(filters?.()))
-
-  const unsubscribe = queryCache().subscribe(() => {
-    setFetches(client().isFetching(filters?.()))
-  })
-
-  onCleanup(unsubscribe)
+  const [fetches, setFetches] = createSignal(0)
+
+  createEffect(() => {
+    setFetches(client().isFetching(filters?.()))
+    const unsubscribe = queryCache().subscribe(() => {
+      setFetches(client().isFetching(filters?.()))
+    })
+    onCleanup(unsubscribe)
+  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-query/src/useIsFetching.ts` around lines 11 - 21, The
subscription to queryCache is created once and won't update when the reactive
client/queryCache changes; wrap the subscribe/unsubscribe logic in a reactive
scope (e.g. createEffect) so it re-subscribes whenever client() or queryCache()
changes and ensure onCleanup unsubscribes the previous listener; specifically,
move the queryCache().subscribe(...) and its onCleanup into a createEffect that
reads queryCache() (or client()) and also re-evaluates
setFetches(client().isFetching(filters?.())) inside the effect so fetches
updates immediately when client, queryCache or filters change (referencing
resolveClient, client, queryCache, setFetches).
packages/solid-query/src/__tests__/QueryClientProvider.test.tsx (1)

180-201: ⚡ Quick win

Add a test for useQueryClientResolver when no QueryClientProvider is present.

The PR's linked issue (#10445) explicitly lists "ensure resolver avoids throws when context is missing/late" as a required test, but no such case is covered here. The existing suite only validates the happy path. It's unclear from the test file alone whether useQueryClientResolver() without a provider throws (like useQueryClient() does), silently returns undefined, or behaves differently — and the expected contract should be locked down by a test.

💡 Example test to add
+  describe('useQueryClientResolver', () => {
+    it('should throw when called outside a QueryClientProvider', () => {
+      const consoleMock = vi
+        .spyOn(console, 'error')
+        .mockImplementation(() => undefined)
+
+      function Page() {
+        useQueryClientResolver()
+        return null
+      }
+
+      // Adjust the expectation to match the actual contract (throw vs. safe no-op)
+      expect(() => render(() => <Page />)).toThrow(
+        'No QueryClient set, use QueryClientProvider to set one',
+      )
+
+      consoleMock.mockRestore()
+    })
+  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-query/src/__tests__/QueryClientProvider.test.tsx` around lines
180 - 201, Add a test that verifies useQueryClientResolver is safe when no
provider is present: render a component that captures resolveClient =
useQueryClientResolver() but do not wrap it with <QueryClientProvider>, then
inside a createRoot/createMemo call invoke resolveClient() and assert that
invoking it does not throw and returns undefined (i.e., the resolver handles
missing/late context); reference useQueryClientResolver, QueryClientProvider,
createRoot and createMemo in the test to locate where to add the new case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/solid-query/src/__tests__/QueryClientProvider.test.tsx`:
- Around line 180-201: Add a test that verifies useQueryClientResolver is safe
when no provider is present: render a component that captures resolveClient =
useQueryClientResolver() but do not wrap it with <QueryClientProvider>, then
inside a createRoot/createMemo call invoke resolveClient() and assert that
invoking it does not throw and returns undefined (i.e., the resolver handles
missing/late context); reference useQueryClientResolver, QueryClientProvider,
createRoot and createMemo in the test to locate where to add the new case.

In `@packages/solid-query/src/useIsFetching.ts`:
- Around line 11-21: The subscription to queryCache is created once and won't
update when the reactive client/queryCache changes; wrap the
subscribe/unsubscribe logic in a reactive scope (e.g. createEffect) so it
re-subscribes whenever client() or queryCache() changes and ensure onCleanup
unsubscribes the previous listener; specifically, move the
queryCache().subscribe(...) and its onCleanup into a createEffect that reads
queryCache() (or client()) and also re-evaluates
setFetches(client().isFetching(filters?.())) inside the effect so fetches
updates immediately when client, queryCache or filters change (referencing
resolveClient, client, queryCache, setFetches).

In `@packages/solid-query/src/useIsMutating.ts`:
- Around line 11-23: The subscription to mutationCache in useIsMutating is
created once and won't track changes if the resolved client changes; wrap the
subscription logic in a createEffect (like useMutationState) that reads
resolveClient()/client() and mutationCache() so it re-subscribes when client
changes, call setMutations(client().isMutating(filters?.())) inside the effect,
and ensure you call onCleanup to unsubscribe the previous
mutationCache.subscribe; update functions/variables involved: resolveClient,
client, mutationCache, setMutations, createEffect, and onCleanup so the
subscription follows client changes.

In `@packages/solid-query/src/useMutation.ts`:
- Around line 24-25: The MutationObserver in useMutation is constructed once
using the memoized client (client = createMemo(() => resolveClient())) and never
updated if the resolved QueryClient changes; modify useMutation so it watches
the client memo and recreates/updates the MutationObserver when client changes
(similar to useBaseQuery's on(client, ...) pattern): use a reactive listener
(e.g., createComputed(on(client, ...))) inside useMutation to dispose the old
MutationObserver and instantiate a new MutationObserver bound to the new
resolveClient() result so the observer always uses the current QueryClient.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 80c0bf91-5702-4412-964a-c1bb97f3c67f

📥 Commits

Reviewing files that changed from the base of the PR and between 41a7ab0 and 9e204dc.

📒 Files selected for processing (9)
  • .changeset/solid-query-client-resolver.md
  • packages/solid-query/src/QueryClientProvider.tsx
  • packages/solid-query/src/__tests__/QueryClientProvider.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

Copy link
Copy Markdown
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.

🧹 Nitpick comments (2)
packages/solid-query/src/__tests__/useIsFetching.test.tsx (1)

264-301: ⚡ Quick win

Make this test verify unsubscribe, not just the displayed count.

The current assertions don’t prove the old queryClient1 query-cache listener was removed. A stale listener would still call back into client() and read the new client after setClient(queryClient2), so this can stay green with a leak. Please assert that the first cache subscription is cleaned up when the client accessor changes.

Example: wrap the first cache subscription and assert its cleanup
   it('should resubscribe when a custom queryClient changes', async () => {
     const queryClient1 = new QueryClient()
     const queryClient2 = new QueryClient()
     const key1 = queryKey()
     const key2 = queryKey()
     const [client, setClient] = createSignal(queryClient1)
+    const queryCache1 = queryClient1.getQueryCache()
+    const originalSubscribe1 = queryCache1.subscribe.bind(queryCache1)
+    const unsubscribe1 = vi.fn()
+
+    vi.spyOn(queryCache1, 'subscribe').mockImplementation((listener) => {
+      const cleanup = originalSubscribe1(listener)
+      return () => {
+        unsubscribe1()
+        cleanup()
+      }
+    })

     function Page() {
       const isFetching = useIsFetching(undefined, client)

       return <div>isFetching: {isFetching()}</div>
@@
     expect(rendered.getByText('isFetching: 1')).toBeInTheDocument()

     setClient(queryClient2)
+    expect(unsubscribe1).toHaveBeenCalledTimes(1)

     expect(rendered.getByText('isFetching: 0')).toBeInTheDocument()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-query/src/__tests__/useIsFetching.test.tsx` around lines 264 -
301, The test should assert that the old QueryClient's cache listener is removed
when the client accessor changes; wrap or spy on
queryClient1.getQueryCache().subscribe (or the subscribe return's unsubscribe
function) before rendering Page (which uses useIsFetching with client accessor),
capture the unsubscribe function or a spy, then after calling
setClient(queryClient2) assert that the unsubscribe was called (or that the
returned subscription is cleaned up) in addition to the displayed count
assertions so you prove the subscription was removed from queryClient1 when Page
switched clients.
packages/solid-query/src/__tests__/useIsMutating.test.tsx (1)

210-245: ⚡ Quick win

Assert old-cache cleanup explicitly in this resubscription test.

This sequence can still pass if the queryClient1 listener leaks, because the stale callback in useIsMutating reads client() when it fires and will see queryClient2 after the swap. Please make the test observe the first subscription’s cleanup directly.

Example: verify the first unsubscribe runs on client switch
   it('should resubscribe when a custom queryClient changes', async () => {
     const queryClient1 = new QueryClient()
     const queryClient2 = new QueryClient()
     const [client, setClient] = createSignal(queryClient1)
+    const mutationCache1 = queryClient1.getMutationCache()
+    const originalSubscribe1 = mutationCache1.subscribe.bind(mutationCache1)
+    const unsubscribe1 = vi.fn()
+
+    vi.spyOn(mutationCache1, 'subscribe').mockImplementation((listener) => {
+      const cleanup = originalSubscribe1(listener)
+      return () => {
+        unsubscribe1()
+        cleanup()
+      }
+    })

     function Page() {
       const isMutating = useIsMutating(undefined, client)

       return <div>mutating: {isMutating()}</div>
@@
     expect(rendered.getByText('mutating: 1')).toBeInTheDocument()

     setClient(queryClient2)
+    expect(unsubscribe1).toHaveBeenCalledTimes(1)

     expect(rendered.getByText('mutating: 0')).toBeInTheDocument()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-query/src/__tests__/useIsMutating.test.tsx` around lines 210 -
245, The test must explicitly assert that the old mutation cache subscription is
cleaned up when swapping clients: wrap or spy on
queryClient1.getMutationCache().subscribe (or the returned unsubscribe) before
rendering Page, record when the unsubscribe is invoked, call
setClient(queryClient2) and assert that the unsubscribe was called (showing the
first subscription was removed) in addition to the existing mutating-count
assertions; reference queryClient1, getMutationCache,
subscribe/returned-unsubscribe, useIsMutating and setClient to locate where to
instrument the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/solid-query/src/__tests__/useIsFetching.test.tsx`:
- Around line 264-301: The test should assert that the old QueryClient's cache
listener is removed when the client accessor changes; wrap or spy on
queryClient1.getQueryCache().subscribe (or the subscribe return's unsubscribe
function) before rendering Page (which uses useIsFetching with client accessor),
capture the unsubscribe function or a spy, then after calling
setClient(queryClient2) assert that the unsubscribe was called (or that the
returned subscription is cleaned up) in addition to the displayed count
assertions so you prove the subscription was removed from queryClient1 when Page
switched clients.

In `@packages/solid-query/src/__tests__/useIsMutating.test.tsx`:
- Around line 210-245: The test must explicitly assert that the old mutation
cache subscription is cleaned up when swapping clients: wrap or spy on
queryClient1.getMutationCache().subscribe (or the returned unsubscribe) before
rendering Page, record when the unsubscribe is invoked, call
setClient(queryClient2) and assert that the unsubscribe was called (showing the
first subscription was removed) in addition to the existing mutating-count
assertions; reference queryClient1, getMutationCache,
subscribe/returned-unsubscribe, useIsMutating and setClient to locate where to
instrument the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2916fe7e-3bfa-4ba1-adb1-64a361016163

📥 Commits

Reviewing files that changed from the base of the PR and between 9e204dc and 0843b31.

📒 Files selected for processing (5)
  • packages/solid-query/src/__tests__/QueryClientProvider.test.tsx
  • packages/solid-query/src/__tests__/useIsFetching.test.tsx
  • packages/solid-query/src/__tests__/useIsMutating.test.tsx
  • packages/solid-query/src/useIsFetching.ts
  • packages/solid-query/src/useIsMutating.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/solid-query/src/tests/QueryClientProvider.test.tsx
  • packages/solid-query/src/useIsFetching.ts

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.

useQueryClient / useContext runs inside createMemo, breaking Solid rules and causing undefined QueryClient (e.g. defaultQueryOptions on lazy routes)

1 participant