Open8

【Remix】ネストしたloaderのnot foundをroot.tsxのError Boundaryでキャッチしたい。

omihirofumiomihirofumi

Remixのloaderで投げた404エラーを、root.tsxに設定したerror boundaryがキャッチしてくれないので原因を調べる。

    "@remix-run/node": "^2.14.0",
    "@remix-run/react": "^2.14.0",
    "@remix-run/serve": "^2.14.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",

やりたいこと。
/hoge/{id}をブラウザにアクセスしたときに、存在しないIDの場合root.tsxのエラーバウンダリーにキャッチさせたい。
キャッチせずにブラウザのNot Foundが出るのを防ぎたい感じです。

omihirofumiomihirofumi

構成とコード

app
├── app.css
├── data.ts
├── root.tsx
└── routes
    ├── contacts.$contactId.tsx
    └── contacts.tsx
root.tsx
import {
    Links,
    Meta,
    Outlet,
    Scripts,
    ScrollRestoration,
    isRouteErrorResponse,
    useRouteError,
} from "@remix-run/react";


export function Layout({ children }: { children: React.ReactNode }) {
    return (
        <html lang="en">
            <head>
                <meta charSet="utf-8" />
                <meta
                    name="viewport"
                    content="width=device-width, initial-scale=1"
                />
                <Meta />
                <Links />
            </head>
            <body>
                {children}
                <ScrollRestoration />
                <Scripts />
            </body>
        </html>
    );
}

export default function App() {
    return <Outlet />;
}

export function ErrorBoundary() {
  const error = useRouteError();

  if (isRouteErrorResponse(error)) {
    return (
      <div>
        <h1>
          {error.status} {error.statusText}
        </h1>
        <p>{error.data}</p>
      </div>
    );
  } else if (error instanceof Error) {
    return (
      <div>
        <h1>Error</h1>
        <p>{error.message}</p>
        <p>The stack trace is:</p>
        <pre>{error.stack}</pre>
      </div>
    );
  } else {
    return <h1>Unknown Error</h1>;
  }
}


contacts.$contactId.tsx
export const loader = async () => {
    throw new Response(null, { status: 404, statusText: "Not Found" });
}
omihirofumiomihirofumi

同ファイル内にError Boundaryを設置してみる。

contacts.$contactId.tsx
+import { isRouteErrorResponse, useRouteError } from "@remix-run/react";

export const loader = async () => {
    throw new Response(null, { status: 404, statusText: "Not Found" });
}

+export function ErrorBoundary() {
+  const error = useRouteError();

+  if (isRouteErrorResponse(error)) {
+    return (
+      <div>
+        <h1>
+          {error.status} {error.statusText}
+        </h1>
+        <p>{error.data}</p>
+      </div>
+    );
+  } else if (error instanceof Error) {
+    return (
+      <div>
+        <h1>Error</h1>
+        <p>{error.message}</p>
+        <p>The stack trace is:</p>
+        <pre>{error.stack}</pre>
+      </div>
+    );
+  } else {
+    return <h1>Unknown Error</h1>;
+  }
+}


ブラウザには/contactsの内容が表示されて、こんなエラーがコンソールに出た。

Matched leaf route at location "/contacts/123" does not have an element or Component. This means it will render an <Outlet /> with a null value by default resulting in an
 "empty" page.

omihirofumiomihirofumi

element or Componentがないよって。って言ってるので、

contacts.contactId.tsx
export const loader = async () => {
    throw new Response(null, { status: 404, statusText: "Not Found" });
}

+export default function Page() {
+    return <h1>test</h1>
+}

// ここに記述していたerror boundaryは削除

キャッチしてくれた。

omihirofumiomihirofumi

remix.run/docs/ja/main/guides/errors#error-handling

Remix will automatically catch errors and render the nearest error boundary for errors thrown while:

  • rendering in the browser
  • rendering on the server
  • in a loader during the initial server-rendered document request
  • in an action during the initial server-rendered document request
  • in a loader during a client-side transition in the browser (Remix serializes the error and sends it over
  • the network to the browser)
  • in an action during a client-side transition in the browser

in a loader during the initial server-rendered document request

レンダーが走らないとrootのエラーバウンダリーで処理されない?

omihirofumiomihirofumi

直接解決するわけではないけど、
api.contacts.$contactId.tsxっていうファイル名にして、apiだよってわかるようにするか〜。
わざわざ/api/contacts/{id}って打つ必要があるので、あまり打たれないかな。
Thanks💪

omihirofumiomihirofumi

remixでAPIだけ生やしたいときの実装はきついのかな。
ソース読んで勉強します。わかったらまたここに投稿する。