From 91ebff5a40b44b395d3ccf01977a27318c9ca67e Mon Sep 17 00:00:00 2001 From: Jacob Ebey Date: Wed, 19 Apr 2023 13:53:32 -0700 Subject: [PATCH 1/2] fix: short circuit links and meta for routes that are not rendered due to errors --- integration/link-test.ts | 60 +++++++++++++++++++++++++++++ packages/remix-react/components.tsx | 35 +++++++++++++++-- 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/integration/link-test.ts b/integration/link-test.ts index 48b69655145..3a9ecc4946c 100644 --- a/integration/link-test.ts +++ b/integration/link-test.ts @@ -220,6 +220,9 @@ test.describe("route module link export", () => {
  • Resource routes
  • +
  • + Errored child route +
  • @@ -471,6 +474,42 @@ test.describe("route module link export", () => { } `, + + "app/routes/parent.jsx": js` + import { Outlet } from "@remix-run/react"; + + export function links() { + return [ + { "data-test-id": "red" }, + ]; + } + + export default function Component() { + return
    ; + } + + export function ErrorBoundary() { + return

    Error Boundary

    ; + } + `, + + "app/routes/parent.child.jsx": js` + import { Outlet } from "@remix-run/react"; + + export function loader() { + throw new Response(null, { status: 404 }); + } + + export function links() { + return [ + { "data-test-id": "blue" }, + ]; + } + + export default function Component() { + return
    ; + } + `, }, }); appFixture = await createAppFixture(fixture); @@ -511,6 +550,17 @@ test.describe("route module link export", () => { expect(stylesheetResponses.length).toEqual(1); }); + test("does not render errored child route links", async ({ page }) => { + let app = new PlaywrightFixture(appFixture, page); + await app.goto("/", true); + await page.click('a[href="/parent/child"]'); + await page.waitForSelector('[data-test-id="/parent:error-boundary"]'); + await page.waitForSelector('[data-test-id="red"]', { state: "attached" }); + await page.waitForSelector('[data-test-id="blue"]', { + state: "detached", + }); + }); + test.describe("no js", () => { test.use({ javaScriptEnabled: false }); @@ -534,6 +584,16 @@ test.describe("route module link export", () => { let locator = page.locator("link[rel=preload][as=image]"); expect(await locator.getAttribute("imagesizes")).toBe("100vw"); }); + + test("does not render errored child route links", async ({ page }) => { + let app = new PlaywrightFixture(appFixture, page); + await app.goto("/parent/child"); + await page.waitForSelector('[data-test-id="/parent:error-boundary"]'); + await page.waitForSelector('[data-test-id="red"]', { state: "attached" }); + await page.waitForSelector('[data-test-id="blue"]', { + state: "detached", + }); + }); }); test.describe("script imports", () => { diff --git a/packages/remix-react/components.tsx b/packages/remix-react/components.tsx index cba0385c267..4d8ef4c4dc7 100644 --- a/packages/remix-react/components.tsx +++ b/packages/remix-react/components.tsx @@ -384,7 +384,14 @@ let fetcherSubmissionWarning = */ export function Links() { let { manifest, routeModules } = useRemixContext(); - let { matches } = useDataRouterStateContext(); + let { errors, matches: routerMatches } = useDataRouterStateContext(); + + let matches = errors + ? routerMatches.slice( + 0, + routerMatches.findIndex((m) => errors![m.route.id]) + 1 + ) + : routerMatches; let links = React.useMemo( () => getLinksForMatches(matches, routeModules, manifest), @@ -569,9 +576,20 @@ function PrefetchPageLinksImpl({ */ function V1Meta() { let { routeModules } = useRemixContext(); - let { matches, loaderData } = useDataRouterStateContext(); + let { + errors, + matches: routerMatches, + loaderData, + } = useDataRouterStateContext(); let location = useLocation(); + let matches = errors + ? routerMatches.slice( + 0, + routerMatches.findIndex((m) => errors![m.route.id]) + 1 + ) + : routerMatches; + let meta: V1_HtmlMetaDescriptor = {}; let parentsData: { [routeId: string]: AppData } = {}; @@ -667,9 +685,20 @@ function V1Meta() { function V2Meta() { let { routeModules } = useRemixContext(); - let { matches: _matches, loaderData } = useDataRouterStateContext(); + let { + errors, + matches: routerMatches, + loaderData, + } = useDataRouterStateContext(); let location = useLocation(); + let _matches = errors + ? routerMatches.slice( + 0, + routerMatches.findIndex((m) => errors![m.route.id]) + 1 + ) + : routerMatches; + let meta: V2_MetaDescriptor[] = []; let leafMeta: V2_MetaDescriptor[] | null = null; let matches: V2_MetaMatches = []; From ce6355dcc685357ac708c6a23241993dddff8e82 Mon Sep 17 00:00:00 2001 From: Jacob Ebey Date: Wed, 19 Apr 2023 13:58:22 -0700 Subject: [PATCH 2/2] add changeset --- .changeset/link-meta-short-circuit.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/link-meta-short-circuit.md diff --git a/.changeset/link-meta-short-circuit.md b/.changeset/link-meta-short-circuit.md new file mode 100644 index 00000000000..c7d925616d5 --- /dev/null +++ b/.changeset/link-meta-short-circuit.md @@ -0,0 +1,5 @@ +--- +"@remix-run/react": patch +--- + +short circuit links and meta for routes that are not rendered due to errors