React Router v7 已發布。 查看文件
根目錄

根路由

「根」路由 (app/root.tsx) 是您的 Remix 應用程式中唯一必要的路由,因為它是您 routes/ 目錄中所有路由的父級,並且負責渲染根 <html> 文件。

除此之外,它基本上與任何其他路由相同,並支援所有標準路由導出

由於根路由管理您的文件,因此它是渲染 Remix 提供的一些「文件層級」元件的適當位置。這些元件應在您的根路由內使用一次,它們包含 Remix 為使您的頁面正常渲染而計算或建構的所有內容。

import type { LinksFunction } from "@remix-run/node"; // or cloudflare/deno
import {
  Links,
  LiveReload,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "@remix-run/react";

import globalStylesheetUrl from "./global-styles.css";

export const links: LinksFunction = () => {
  return [{ rel: "stylesheet", href: globalStylesheetUrl }];
};

export default function App() {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />

        {/* All `meta` exports on all routes will render here */}
        <Meta />

        {/* All `link` exports on all routes will render here */}
        <Links />
      </head>
      <body>
        {/* Child routes render here */}
        <Outlet />

        {/* Manages scroll position for client-side transitions */}
        {/* If you use a nonce-based content security policy for scripts, you must provide the `nonce` prop. Otherwise, omit the nonce prop as shown here. */}
        <ScrollRestoration />

        {/* Script tags go here */}
        {/* If you use a nonce-based content security policy for scripts, you must provide the `nonce` prop. Otherwise, omit the nonce prop as shown here. */}
        <Scripts />

        {/* Sets up automatic reload when you change code */}
        {/* and only does anything during development */}
        {/* If you use a nonce-based content security policy for scripts, you must provide the `nonce` prop. Otherwise, omit the nonce prop as shown here. */}
        <LiveReload />
      </body>
    </html>
  );
}

Layout 導出

由於根路由管理所有路由的文件,它也支援額外的可選 Layout 導出。您可以在此 RFC 中閱讀詳細資訊,但版面配置路由有兩個用途

  • 避免在您的根元件、HydrateFallbackErrorBoundary 中重複您的文件/「應用程式外殼」
  • 避免當在根元件/HydrateFallback/ErrorBoundary 之間切換時,React 重新掛載您的應用程式外殼元素,如果 React 從您的 <Links> 元件中移除並重新添加 <link rel="stylesheet"> 標籤,可能會導致 FOUC。
import {
  Links,
  LiveReload,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "@remix-run/react";

export function Layout({ children }) {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />
        <Meta />
        <Links />
      </head>
      <body>
        {/* children will be the root Component, ErrorBoundary, or HydrateFallback */}
        {children}
        <Scripts />
        <ScrollRestoration />
        <LiveReload />
      </body>
    </html>
  );
}

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

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

  if (isRouteErrorResponse(error)) {
    return (
      <>
        <h1>
          {error.status} {error.statusText}
        </h1>
        <p>{error.data}</p>
      </>
    );
  }

  return (
    <>
      <h1>Error!</h1>
      <p>{error?.message ?? "Unknown error"}</p>
    </>
  );
}

關於 Layout 元件中的 useLoaderData 的注意事項

useLoaderData 不允許在 ErrorBoundary 元件中使用,因為它是用於正常路徑路由渲染的,並且其類型定義內建了 loader 已成功執行並返回某些內容的假設。在 ErrorBoundary 中,這個假設不成立,因為可能是 loader 拋出錯誤並觸發了邊界!為了在 ErrorBoundary 中存取 loader 資料,您可以使用 useRouteLoaderData,它會考慮 loader 資料可能為 undefined 的情況。

因為您的 Layout 組件同時用於成功和錯誤流程,因此也存在相同的限制。如果需要在您的 Layout 中根據請求是否成功來分支邏輯,您可以使用 useRouteLoaderData("root")useRouteError()

由於您的 <Layout> 組件用於渲染 ErrorBoundary,您應該非常謹慎地確保您可以渲染您的 ErrorBoundary 而不會遇到任何渲染錯誤。如果您的 Layout 在嘗試渲染邊界時拋出另一個錯誤,則無法使用它,並且您的 UI 將回退到非常簡潔的內建預設 ErrorBoundary

export function Layout({
  children,
}: {
  children: React.ReactNode;
}) {
  const data = useRouteLoaderData("root");
  const error = useRouteError();

  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />
        <Meta />
        <Links />
        <style
          dangerouslySetInnerHTML={{
            __html: `
              :root {
                --themeVar: ${
                  data?.themeVar || defaultThemeVar
                }
              }
            `,
          }}
        />
      </head>
      <body>
        {data ? (
          <Analytics token={data.analyticsToken} />
        ) : null}
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

另請參閱

文件和範例授權於 MIT