Skip to content

Commit

Permalink
Fix initial hydration matching when using a basename (#9584)
Browse files Browse the repository at this point in the history
  • Loading branch information
brophdawg11 authored Jun 13, 2024
1 parent 140983f commit 1802738
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/shiny-bags-breathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/react": patch
---

Fix a bug where hydration wouldn't work right when using child routes and hydrate fallbacks with a basename
92 changes: 83 additions & 9 deletions integration/vite-basename-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import {
} from "./helpers/vite.js";
import { js } from "./helpers/create-fixture.js";

const files = {
"app/routes/_index.tsx": String.raw`
const sharedFiles = {
"app/routes/_index.tsx": js`
import { useState, useEffect } from "react";
import { Link } from "@remix-run/react"
Expand All @@ -36,7 +36,7 @@ const files = {
);
}
`,
"app/routes/other.tsx": String.raw`
"app/routes/other.tsx": js`
import { useLoaderData } from "@remix-run/react";
export const loader = () => {
Expand Down Expand Up @@ -93,7 +93,7 @@ const customServerFile = ({
base = base ?? "/mybase/";
basename = basename ?? base;

return String.raw`
return js`
import { createRequestHandler } from "@remix-run/express";
import { installGlobals } from "@remix-run/node";
import express from "express";
Expand Down Expand Up @@ -139,15 +139,17 @@ test.describe("Vite base / Remix basename / Vite dev", () => {
base,
basename,
startServer,
files,
}: {
base: string;
basename: string;
startServer?: boolean;
files?: Record<string, string>;
}) {
port = await getPort();
cwd = await createProject({
"vite.config.js": await viteConfigFile({ port, base, basename }),
...files,
...(files || sharedFiles),
});
if (startServer !== false) {
stop = await viteDev({ cwd, port, basename });
Expand Down Expand Up @@ -178,6 +180,78 @@ test.describe("Vite base / Remix basename / Vite dev", () => {
"`basename` config must begin with `base` for the default Vite dev server."
);
});

test("works with child routes using client loaders", async ({ page }) => {
let basename = "/mybase/";
await setup({
base: basename,
basename,
files: {
...sharedFiles,
"app/routes/parent.tsx": js`
import { Outlet } from '@remix-run/react'
export default function Parent() {
return <div id="parent"><Outlet /></div>;
}
`,
"app/routes/parent.child.tsx": js`
import { useState, useEffect } from "react";
import { useLoaderData } from '@remix-run/react'
export async function clientLoader() {
await new Promise(resolve => setTimeout(resolve, 500))
return "CHILD"
}
export function HydrateFallback() {
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
return (
<>
<p id="loading">Loading...</p>
<p data-mounted>Mounted: {mounted ? "yes" : "no"}</p>
</>
);
}
export default function Child() {
const data = useLoaderData()
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
return (
<>
<div id="child">{data}</div>;
<p data-mounted>Mounted: {mounted ? "yes" : "no"}</p>
</>
);
}
`,
},
});

let hydrationErrors: Error[] = [];
page.on("pageerror", (error) => {
if (
error.message.includes("Hydration failed") ||
error.message.includes("error while hydrating") ||
error.message.includes("does not match server-rendered HTML")
) {
hydrationErrors.push(error);
}
});

// setup: initial render at basename
await page.goto(`http://localhost:${port}${basename}parent/child`, {
waitUntil: "domcontentloaded",
});

await expect(page.locator("#parent")).toBeDefined();
await expect(page.locator("#loading")).toContainText("Loading...");
await expect(page.locator("[data-mounted]")).toHaveText("Mounted: yes");

expect(hydrationErrors).toEqual([]);

await page.waitForSelector("#child");
await expect(page.locator("#child")).toHaveText("CHILD");
await expect(page.locator("[data-mounted]")).toHaveText("Mounted: yes");
});
});

test.describe("Vite base / Remix basename / express dev", async () => {
Expand All @@ -198,7 +272,7 @@ test.describe("Vite base / Remix basename / express dev", async () => {
cwd = await createProject({
"vite.config.js": await viteConfigFile({ port, base, basename }),
"server.mjs": customServerFile({ port, basename }),
...files,
...sharedFiles,
});
if (startServer !== false) {
stop = await customDev({ cwd, port, basename });
Expand Down Expand Up @@ -323,7 +397,7 @@ test.describe("Vite base / Remix basename / vite build", () => {
port = await getPort();
cwd = await createProject({
"vite.config.js": await viteConfigFile({ port, base, basename }),
...files,
...sharedFiles,
});
viteBuild({ cwd });
if (startServer !== false) {
Expand Down Expand Up @@ -370,7 +444,7 @@ test.describe("Vite base / Remix basename / express build", async () => {
cwd = await createProject({
"vite.config.js": await viteConfigFile({ port, base, basename }),
"server.mjs": customServerFile({ port, base, basename }),
...files,
...sharedFiles,
});
viteBuild({ cwd });
if (startServer !== false) {
Expand Down Expand Up @@ -427,7 +501,7 @@ test.describe("Vite base / Remix basename / express build", async () => {
const port = ${port};
app.listen(port, () => console.log('http://localhost:' + port));
`,
...files,
...sharedFiles,
});

viteBuild({ cwd });
Expand Down
6 changes: 5 additions & 1 deletion packages/remix-react/browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,11 @@ export function RemixBrowser(_props: RemixBrowserProps): ReactElement {
...window.__remixContext.state,
loaderData: { ...window.__remixContext.state.loaderData },
};
let initialMatches = matchRoutes(routes, window.location);
let initialMatches = matchRoutes(
routes,
window.location,
window.__remixContext.basename
);
if (initialMatches) {
for (let match of initialMatches) {
let routeId = match.route.id;
Expand Down

0 comments on commit 1802738

Please sign in to comment.