diff --git a/.changeset/solid-query-suspense-ensure-data.md b/.changeset/solid-query-suspense-ensure-data.md new file mode 100644 index 00000000000..d1d72d27018 --- /dev/null +++ b/.changeset/solid-query-suspense-ensure-data.md @@ -0,0 +1,5 @@ +--- +'@tanstack/solid-query': patch +--- + +fix(solid-query): avoid triggering Suspense when data is already cached (e.g. via `ensureQueryData`) diff --git a/packages/solid-query/src/__tests__/suspense.test.tsx b/packages/solid-query/src/__tests__/suspense.test.tsx index b2df435dfc2..eadd501984f 100644 --- a/packages/solid-query/src/__tests__/suspense.test.tsx +++ b/packages/solid-query/src/__tests__/suspense.test.tsx @@ -908,6 +908,47 @@ describe("useQuery's in Suspense mode", () => { consoleMock.mockRestore() }) + // https://github.com/TanStack/query/issues/9955 + it('should not trigger Suspense when data was preloaded via ensureQueryData', async () => { + const key = queryKey() + + const ensurePromise = queryClient.ensureQueryData({ + queryKey: key, + queryFn: () => sleep(10).then(() => 'preloaded'), + staleTime: Infinity, + }) + await vi.advanceTimersByTimeAsync(10) + await ensurePromise + + let fallbackMounted = false + + function Page() { + const state = useQuery(() => ({ + queryKey: key, + queryFn: () => sleep(10).then(() => 'fresh'), + staleTime: Infinity, + })) + + return
data: {state.data}
+ } + + function Fallback() { + fallbackMounted = true + return <>loading + } + + const rendered = render(() => ( + + }> + + + + )) + + expect(rendered.getByText('data: preloaded')).toBeInTheDocument() + expect(fallbackMounted).toBe(false) + }) + it('should render the correct amount of times in Suspense mode when gcTime is set to 0', async () => { const key = queryKey() let state: UseQueryResult | null = null diff --git a/packages/solid-query/src/useBaseQuery.ts b/packages/solid-query/src/useBaseQuery.ts index 773d0719e0c..e45063d123a 100644 --- a/packages/solid-query/src/useBaseQuery.ts +++ b/packages/solid-query/src/useBaseQuery.ts @@ -10,6 +10,7 @@ import { createSignal, on, onCleanup, + untrack, } from 'solid-js' import { createStore, reconcile, unwrap } from 'solid-js/store' import { useQueryClient } from './QueryClientProvider' @@ -377,6 +378,14 @@ export function useBaseQuery< ): any { if (prop === 'data') { if (state.data !== undefined) { + // When data is already in the store and no fetch is in-flight (e.g. + // it was preloaded via `ensureQueryData`), avoid reading the resource + // because its initial pending state would otherwise trigger Suspense + // on the synchronous-resolve microtask gap. See #9955. + // `untrack` keeps `isFetching` from leaking into the data subscriber. + if (!untrack(() => state.isFetching)) { + return state.data + } return queryResource.latest?.data } return queryResource()?.data