diff --git a/e2e/ssr-hydration.spec.ts b/e2e/ssr-hydration.spec.ts index 99f800a46c..e64e8772bb 100644 --- a/e2e/ssr-hydration.spec.ts +++ b/e2e/ssr-hydration.spec.ts @@ -28,6 +28,12 @@ test('SSR community HTML hydrates SPA navigation and client search', async ({ page, request, }) => { + // A throw anywhere in the client runtime silently kills the rest of + // hydration, so the whole journey below is only meaningful if it stays + // error-free. + const clientErrors: Array = [] + page.on('pageerror', (error) => clientErrors.push(error.message)) + await ensurePrimaryUserExists() await seedCommunityListingInE2eDatabase({ ...alphaListing, @@ -205,4 +211,6 @@ test('SSR community HTML hydrates SPA navigation and client search', async ({ await expect(page).toHaveURL(/\/community$/) await expect(page.getByText(betaListing.description)).toBeVisible() await expect(page.getByRole('heading', { name: 'Not Found' })).toHaveCount(0) + + expect(clientErrors).toEqual([]) }) diff --git a/packages/worker/client/routes/community-detail.tsx b/packages/worker/client/routes/community-detail.tsx index 2aae38da7d..1e57373467 100644 --- a/packages/worker/client/routes/community-detail.tsx +++ b/packages/worker/client/routes/community-detail.tsx @@ -589,7 +589,12 @@ export function CommunityDetailRoute(handle: Handle) { shellLoadedForListingId = listingId shellStatus = 'ready' if (stargazersLoadedForListingId !== listingId) { - void loadStargazers(listingId) + // This runs during render, and `loadStargazers` calls + // `handle.update()` before its first await. The runtime only wires + // up a component's update scheduler after its first render + // returns, so calling it inline throws instead of scheduling: + // https://github.com/remix-run/remix/issues/11642 + handle.queueTask(() => void loadStargazers(listingId)) } return true }