Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions examples/app-router-cloudflare/app/effect-test/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"use client";

import { useEffect, useState } from "react";

// Regression test for https://github.com/cloudflare/vinext/issues/695
// useEffect callbacks were never firing after RSC hydration because
// createFromReadableStream was awaited before being passed to hydrateRoot,
// which blocked hydration until the entire RSC stream was consumed.
export default function EffectTestPage() {
const [fired, setFired] = useState(false);
const [count, setCount] = useState(0);

useEffect(() => {
setFired(true);
}, []);

return (
<div>
<p data-testid="effect-status">{fired ? "effect-fired" : "effect-pending"}</p>
<p data-testid="count">Count: {count}</p>
<button data-testid="increment" onClick={() => setCount((c) => c + 1)}>
Increment
</button>
</div>
);
}
2 changes: 1 addition & 1 deletion packages/vinext/src/server/app-browser-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ async function main(): Promise<void> {
registerServerActionCallback();

const rscStream = await readInitialRscStream();
const root = await createFromReadableStream(rscStream);
const root = createFromReadableStream(rscStream);

reactRoot = hydrateRoot(
document,
Expand Down
13 changes: 13 additions & 0 deletions tests/e2e/cloudflare-workers/hydration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ test.describe("Cloudflare Workers Hydration", () => {
void consoleErrors;
});

// Regression test for https://github.com/cloudflare/vinext/issues/695
// createFromReadableStream was awaited before hydrateRoot, blocking effects.
test("useEffect fires after RSC hydration", async ({ page, consoleErrors }) => {
await page.goto(`${BASE}/effect-test`);

// useEffect should fire and update the status from "effect-pending" to "effect-fired"
await expect(page.locator('[data-testid="effect-status"]')).toHaveText("effect-fired", {
timeout: 10_000,
});

void consoleErrors;
});

test("page with timestamp hydrates without mismatch", async ({ page, consoleErrors }) => {
// This test specifically verifies the fix for GitHub issue #61.
// The home page has a timestamp that would cause hydration mismatch
Expand Down
Loading