Skip to content

Commit e9a17ed

Browse files
authored
feat(react-router): add unstable_onError prop to RouterProvider for client side error reporting (#14162)
1 parent 0569bf7 commit e9a17ed

File tree

9 files changed

+625
-26
lines changed

9 files changed

+625
-26
lines changed

.changeset/cold-geese-turn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
[UNSTABLE] Add `<RouterProvider unstable_onError>`/`<HydratedRouter unstable_onError>` prop for client side error reporting

integration/browser-entry-test.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,70 @@ test("allows users to pass a client side context to HydratedRouter", async ({
129129

130130
appFixture.close();
131131
});
132+
133+
test("allows users to pass an onError function to HydratedRouter", async ({
134+
page,
135+
browserName,
136+
}) => {
137+
let fixture = await createFixture({
138+
files: {
139+
"app/entry.client.tsx": js`
140+
import { HydratedRouter } from "react-router/dom";
141+
import { startTransition, StrictMode } from "react";
142+
import { hydrateRoot } from "react-dom/client";
143+
144+
startTransition(() => {
145+
hydrateRoot(
146+
document,
147+
<StrictMode>
148+
<HydratedRouter
149+
unstable_onError={(error, errorInfo) => {
150+
console.log(error.message, JSON.stringify(errorInfo))
151+
}}
152+
/>
153+
</StrictMode>
154+
);
155+
});
156+
`,
157+
"app/routes/_index.tsx": js`
158+
import { Link } from "react-router";
159+
export default function Index() {
160+
return <Link to="/page">Go to Page</Link>;
161+
}
162+
`,
163+
"app/routes/page.tsx": js`
164+
export default function Page() {
165+
throw new Error("Render error");
166+
}
167+
export function ErrorBoundary({ error }) {
168+
return <h1 data-error>Error: {error.message}</h1>
169+
}
170+
`,
171+
},
172+
});
173+
174+
let logs: string[] = [];
175+
page.on("console", (msg) => logs.push(msg.text()));
176+
177+
let appFixture = await createAppFixture(fixture);
178+
let app = new PlaywrightFixture(appFixture, page);
179+
180+
await app.goto("/", true);
181+
await page.click('a[href="/page"]');
182+
await page.waitForSelector("[data-error]");
183+
184+
expect(await app.getHtml()).toContain("Error: Render error");
185+
expect(logs.length).toBe(2);
186+
// First one is react logging the error
187+
if (browserName === "firefox") {
188+
expect(logs[0]).toContain("Error");
189+
} else {
190+
expect(logs[0]).toContain("Error: Render error");
191+
}
192+
expect(logs[0]).not.toContain("componentStack");
193+
// Second one is ours
194+
expect(logs[1]).toContain("Render error");
195+
expect(logs[1]).toContain('"componentStack":');
196+
197+
appFixture.close();
198+
});

0 commit comments

Comments
 (0)