diff --git a/.changeset/solid-query-client-resolver.md b/.changeset/solid-query-client-resolver.md new file mode 100644 index 00000000000..ef5ae0eb0f1 --- /dev/null +++ b/.changeset/solid-query-client-resolver.md @@ -0,0 +1,5 @@ +--- +'@tanstack/solid-query': patch +--- + +Resolve the query client context outside reactive memo callbacks. diff --git a/packages/solid-query/src/QueryClientProvider.tsx b/packages/solid-query/src/QueryClientProvider.tsx index 6cde56f22d2..fb461c9eafb 100644 --- a/packages/solid-query/src/QueryClientProvider.tsx +++ b/packages/solid-query/src/QueryClientProvider.tsx @@ -5,12 +5,15 @@ import { useContext, } from 'solid-js' import type { QueryClient } from './QueryClient' -import type { JSX } from 'solid-js' +import type { Accessor, JSX } from 'solid-js' export const QueryClientContext = createContext< (() => QueryClient) | undefined >(undefined) +const queryClientContextError = + 'No QueryClient set, use QueryClientProvider to set one' + export const useQueryClient = (queryClient?: QueryClient) => { if (queryClient) { return queryClient @@ -18,12 +21,31 @@ export const useQueryClient = (queryClient?: QueryClient) => { const client = useContext(QueryClientContext) if (!client) { - throw new Error('No QueryClient set, use QueryClientProvider to set one') + throw new Error(queryClientContextError) } return client() } +export const useQueryClientResolver = ( + queryClient?: Accessor, +): Accessor => { + const contextClient = useContext(QueryClientContext) + + return () => { + const resolvedClient = queryClient?.() + if (resolvedClient) { + return resolvedClient + } + + if (!contextClient) { + throw new Error(queryClientContextError) + } + + return contextClient() + } +} + export type QueryClientProviderProps = { client: QueryClient children?: JSX.Element diff --git a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx index e96be03cd24..abe734117a7 100644 --- a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx +++ b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx @@ -2,7 +2,9 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { render } from '@solidjs/testing-library' import { QueryCache } from '@tanstack/query-core' import { queryKey, sleep } from '@tanstack/query-test-utils' +import { createMemo, createRoot } from 'solid-js' import { QueryClient, QueryClientProvider, useQuery, useQueryClient } from '..' +import { useQueryClientResolver } from '../QueryClientProvider' describe('QueryClientProvider', () => { beforeEach(() => { @@ -174,4 +176,27 @@ describe('QueryClientProvider', () => { consoleMock.mockRestore() }) + + it('creates a query client resolver that is safe to call in reactive callbacks', () => { + const queryClient = new QueryClient() + let resolveClient!: () => QueryClient + + function Page() { + resolveClient = useQueryClientResolver() + return null + } + + render(() => ( + + + + )) + + createRoot((dispose) => { + const client = createMemo(() => resolveClient()) + + expect(client()).toBe(queryClient) + dispose() + }) + }) }) diff --git a/packages/solid-query/src/useBaseQuery.ts b/packages/solid-query/src/useBaseQuery.ts index 773d0719e0c..42096176f67 100644 --- a/packages/solid-query/src/useBaseQuery.ts +++ b/packages/solid-query/src/useBaseQuery.ts @@ -12,7 +12,7 @@ import { onCleanup, } from 'solid-js' import { createStore, reconcile, unwrap } from 'solid-js/store' -import { useQueryClient } from './QueryClientProvider' +import { useQueryClientResolver } from './QueryClientProvider' import { useIsRestoring } from './isRestoring' import type { UseBaseQueryOptions } from './types' import type { Accessor, Signal } from 'solid-js' @@ -115,7 +115,8 @@ export function useBaseQuery< ) { type ResourceData = QueryObserverResult - const client = createMemo(() => useQueryClient(queryClient?.())) + const resolveClient = useQueryClientResolver(queryClient) + const client = createMemo(() => resolveClient()) const isRestoring = useIsRestoring() // There are times when we run a query on the server but the resource is never read // This could lead to times when the queryObserver is unsubscribed before the resource has loaded diff --git a/packages/solid-query/src/useIsFetching.ts b/packages/solid-query/src/useIsFetching.ts index e118a5d0762..7733e22ecc2 100644 --- a/packages/solid-query/src/useIsFetching.ts +++ b/packages/solid-query/src/useIsFetching.ts @@ -1,5 +1,5 @@ import { createMemo, createSignal, onCleanup } from 'solid-js' -import { useQueryClient } from './QueryClientProvider' +import { useQueryClientResolver } from './QueryClientProvider' import type { QueryFilters } from '@tanstack/query-core' import type { QueryClient } from './QueryClient' import type { Accessor } from 'solid-js' @@ -8,7 +8,8 @@ export function useIsFetching( filters?: Accessor, queryClient?: Accessor, ): Accessor { - const client = createMemo(() => useQueryClient(queryClient?.())) + const resolveClient = useQueryClientResolver(queryClient) + const client = createMemo(() => resolveClient()) const queryCache = createMemo(() => client().getQueryCache()) const [fetches, setFetches] = createSignal(client().isFetching(filters?.())) diff --git a/packages/solid-query/src/useIsMutating.ts b/packages/solid-query/src/useIsMutating.ts index 57ec870e459..fd2db925578 100644 --- a/packages/solid-query/src/useIsMutating.ts +++ b/packages/solid-query/src/useIsMutating.ts @@ -1,5 +1,5 @@ import { createMemo, createSignal, onCleanup } from 'solid-js' -import { useQueryClient } from './QueryClientProvider' +import { useQueryClientResolver } from './QueryClientProvider' import type { MutationFilters } from '@tanstack/query-core' import type { QueryClient } from './QueryClient' import type { Accessor } from 'solid-js' @@ -8,7 +8,8 @@ export function useIsMutating( filters?: Accessor, queryClient?: Accessor, ): Accessor { - const client = createMemo(() => useQueryClient(queryClient?.())) + const resolveClient = useQueryClientResolver(queryClient) + const client = createMemo(() => resolveClient()) const mutationCache = createMemo(() => client().getMutationCache()) const [mutations, setMutations] = createSignal( diff --git a/packages/solid-query/src/useMutation.ts b/packages/solid-query/src/useMutation.ts index 056766a6433..2a2a8596520 100644 --- a/packages/solid-query/src/useMutation.ts +++ b/packages/solid-query/src/useMutation.ts @@ -1,7 +1,7 @@ import { MutationObserver, noop, shouldThrowError } from '@tanstack/query-core' import { createComputed, createMemo, on, onCleanup } from 'solid-js' import { createStore } from 'solid-js/store' -import { useQueryClient } from './QueryClientProvider' +import { useQueryClientResolver } from './QueryClientProvider' import type { DefaultError } from '@tanstack/query-core' import type { QueryClient } from './QueryClient' import type { @@ -21,7 +21,8 @@ export function useMutation< options: UseMutationOptions, queryClient?: Accessor, ): UseMutationResult { - const client = createMemo(() => useQueryClient(queryClient?.())) + const resolveClient = useQueryClientResolver(queryClient) + const client = createMemo(() => resolveClient()) const observer = new MutationObserver< TData, diff --git a/packages/solid-query/src/useMutationState.ts b/packages/solid-query/src/useMutationState.ts index 2a405a1ae4b..cf5732c35e9 100644 --- a/packages/solid-query/src/useMutationState.ts +++ b/packages/solid-query/src/useMutationState.ts @@ -1,6 +1,6 @@ import { createEffect, createMemo, createSignal, onCleanup } from 'solid-js' import { replaceEqualDeep } from '@tanstack/query-core' -import { useQueryClient } from './QueryClientProvider' +import { useQueryClientResolver } from './QueryClientProvider' import type { Mutation, MutationCache, @@ -31,7 +31,8 @@ export function useMutationState( options: Accessor> = () => ({}), queryClient?: Accessor, ): Accessor> { - const client = createMemo(() => useQueryClient(queryClient?.())) + const resolveClient = useQueryClientResolver(queryClient) + const client = createMemo(() => resolveClient()) const mutationCache = createMemo(() => client().getMutationCache()) const [result, setResult] = createSignal( diff --git a/packages/solid-query/src/useQueries.ts b/packages/solid-query/src/useQueries.ts index 1e5592775db..a0abe68fc1c 100644 --- a/packages/solid-query/src/useQueries.ts +++ b/packages/solid-query/src/useQueries.ts @@ -11,7 +11,7 @@ import { onCleanup, onMount, } from 'solid-js' -import { useQueryClient } from './QueryClientProvider' +import { useQueryClientResolver } from './QueryClientProvider' import { useIsRestoring } from './isRestoring' import type { SolidQueryOptions, UseQueryResult } from './types' import type { Accessor } from 'solid-js' @@ -196,7 +196,8 @@ export function useQueries< }>, queryClient?: Accessor, ): TCombinedResult { - const client = createMemo(() => useQueryClient(queryClient?.())) + const resolveClient = useQueryClientResolver(queryClient) + const client = createMemo(() => resolveClient()) const isRestoring = useIsRestoring() const defaultedQueries = createMemo(() =>