fix(solid-query): don't hang async SSR when a disabled query is rendered#10923
fix(solid-query): don't hang async SSR when a disabled query is rendered#10923naveentehrpariya wants to merge 2 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughRemoves the server-side ChangesSSR Disabled Query Promise Fix
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/solid-query/src/__tests__/ssr.test.tsx (1)
16-44: ⚡ Quick winConsider adding a complementary test for enabled queries.
The test correctly verifies that disabled queries no longer leak
promiseduring SSR. To ensure the fix doesn't break the normal SSR path, consider adding a second test case that verifies an enabled query (one that actually fetches) still hydrates correctly on the server.🧪 Suggested additional test case
+ it('resolves an enabled query and hydrates correctly', async () => { + const client = new QueryClient() + let state: UseQueryResult<string> | undefined + + function Page() { + const query = useQuery(() => ({ + queryKey: ['enabled-ssr'], + queryFn: () => Promise.resolve('fetched-data'), + enabled: true, + })) + state = query + return <div>data: {String(query.data)}</div> + } + + const rendered = render(() => ( + <QueryClientProvider client={client}> + <Page /> + </QueryClientProvider> + )) + + await waitFor(() => rendered.getByText('data: fetched-data')) + + // Enabled queries should still hydrate with data, but without + // non-serializable refetch/promise + expect(state!.data).toBe('fetched-data') + expect(state!.refetch).toBeUndefined() + expect(state!.promise).toBeUndefined() + })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/solid-query/src/__tests__/ssr.test.tsx` around lines 16 - 44, Add a complementary test in the same file that mirrors the disabled-query SSR test but uses an enabled query to ensure normal SSR/hydration still works: create a new it(...) that instantiates QueryClient, renders a Page component which calls useQuery with enabled: true (or omit enabled) and a queryFn resolving to 'data', capture the returned UseQueryResult in the same state variable, render inside QueryClientProvider, wait for the rendered output to show 'data: data', and assert that state!.refetch and state!.promise are present/defined as appropriate for an enabled query; use the existing symbols QueryClient, QueryClientProvider, useQuery, Page, rendered, state to locate where to add the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/solid-query/src/__tests__/ssr.test.tsx`:
- Around line 16-44: Add a complementary test in the same file that mirrors the
disabled-query SSR test but uses an enabled query to ensure normal SSR/hydration
still works: create a new it(...) that instantiates QueryClient, renders a Page
component which calls useQuery with enabled: true (or omit enabled) and a
queryFn resolving to 'data', capture the returned UseQueryResult in the same
state variable, render inside QueryClientProvider, wait for the rendered output
to show 'data: data', and assert that state!.refetch and state!.promise are
present/defined as appropriate for an enabled query; use the existing symbols
QueryClient, QueryClientProvider, useQuery, Page, rendered, state to locate
where to add the test.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0ea170c7-f883-4758-b472-09181db5c68a
📒 Files selected for processing (3)
.changeset/solid-query-ssr-disabled-hang.mdpackages/solid-query/src/__tests__/ssr.test.tsxpackages/solid-query/src/useBaseQuery.ts
|
Added a complementary test for enabled queries on the server — it asserts the query resolves with data and that the serialized result still carries neither |
🎯 Changes
Resolves #10907
A
useQuerywithenabled: falsehangsrenderToStringAsync(and SolidStart's async/stream SSR) as soon as the query is rendered.Root cause: on the server,
useBaseQueryforcesexperimental_prefetchInRender = true, so every observer result carries a.promise.hydratableObserverResult()stripsrefetchbecause functions can't be serialized, but it predatesexperimental_prefetchInRenderand doesn't strippromise. The promise therefore ends up in the resolved resource value, and Solid's SSR serializer (seroval) awaits embedded promises — for a disabled query that promise never settles, so serialization (and the render) hangs forever.I verified this mechanism empirically against the published
@tanstack/solid-query@5.101.0build using the standalone repro from #10907: the resource fetcher actually resolves immediately (isLoadingisfalsefor a disabled query) — the hang happens afterwards, during serialization of the resolved value. StrippingpromisefromhydratableObserverResult()makesrenderToStringAsyncsettle with the expected markup (data=undefined) and a clean hydration script.Fix: strip
promisefrom the hydratable result alongsiderefetch. The client rebuilds the result (including its ownpromise) from the hydrated query state, so nothing is lost.Also adds the first SSR-path test for solid-query (
isServermocked totrue), asserting the resolved result of a disabled query carries neitherrefetchnorpromise— it fails without the fix.✅ Checklist
pnpm run test:pr. (solid-query package: 320 tests + typecheck pass, eslint clean)🚀 Release Impact
Summary by CodeRabbit
Bug Fixes
Tests