💬
Remixの共通レイアウトにHeaderコンポーネントを入れるには?
Remixのルートにはapp\root.tsx
共通レイアウトをいれますが、Appと場合によってはLayoutも定義されています。
ではHeaderコンポーネントを定義したらどこに配置すればいいでしょうか?
- Headerコンポーネントを作る
- rootで呼び出す
- AppにHeaderを入れる
1. Headerコンポーネントを作る
app
ディレクトリにcomponents
ディレクトリを作成します。そこにHeader.tsx
を作ります。
ログイン機能をつける前提で、ログインしていたら右上にアイコンがでます。
app\components\Header.tsx
app\components\Header.tsx
import {
UserButton,
useAuth,
SignInButton
} from "@clerk/remix";
import { Link } from "@remix-run/react";
export default function Index() {
const { isLoaded, userId } = useAuth();
if (!isLoaded) {
return <div>Loading...</div>; // Handle loading state
}
return (
<div>
<header className="flex h-16 w-full items-center border-b px-4 md:px-6">
<Link className="inset-y-0 flex items-center gap-2 font-semibold" to="#">
Acme Inc
</Link>
<nav className="flex flex-1 justify-center font-medium">
<Link className="mx-2" to="#">
Home
</Link>
<Link className="mx-2" to="#">
About
</Link>
<Link className="mx-2" to="#">
Contact
</Link>
</nav>
<div>
{userId ? (
<UserButton /> // Renders user button when logged in
) : (
<SignInButton>Sign in with Clerk</SignInButton> // Button to initiate sign-in
)}
</div>
</header>
</div>
);
}
2. rootでHeaderコンポーネントを呼び出す
app\root.tsx
app\root.tsx
+import Header from "~/components/Header";
3. AppにHeaderを入れる
なぜ `App` に `Header` を配置するのか?
Header
コンポーネントを App
コンポーネントに配置する主な理由は以下の通りです:
-
認証コンテキストの維持:
-
Header
にはログイン機能が組み込まれており(UserButton
、SignInButton
を使用)、これはClerkApp
から提供される認証コンテキストに依存します。 -
ClerkApp
によって提供される認証状態を、App
とその子コンポーネントが利用できます。Header
をApp
内に配置することで、認証情報へのアクセスが保証され、認証状態に基づくUIの適切な更新が可能になります。
-
-
全ページでの一貫した表示:
-
App
はアプリケーションのすべてのページの基本となるコンポーネントです。Outlet
を通じて描画されるすべてのページに対してHeader
が表示されるようにします。 - 各ページへの移動があっても
Header
が常に表示されるため、ユーザーに一貫したナビゲーション体験を提供できます。
-
-
パフォーマンスと最適化:
-
App
内でHeader
をレンダリングすることにより、ページ遷移時にHeader
のDOMが再生成されることなく、Reactが必要な部分だけを再描画します。これにより、パフォーマンスが向上し、不要なリソースのロードが減少します。
-
-
設計のシンプルさ:
-
Header
をApp
の直下に配置することで、デザインとコードがシンプルになり、管理が容易になります。新しい開発者がプロジェクトに参加した場合でも、コードの理解と修正がしやすくなります。
-
これらの理由から、App
コンポーネント内にHeader
を配置することは、一貫性、パフォーマンス、アクセス性、そして管理の容易さを考慮した最適な選択となります。
app\root.tsx
app\root.tsx
import {
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration
} from "@remix-run/react";
import type { LoaderFunction,LinksFunction } from "@remix-run/node";
import { rootAuthLoader } from "@clerk/remix/ssr.server";
import { ClerkApp, ClerkErrorBoundary } from "@clerk/remix";
import Header from "~/components/Header";
export const loader: LoaderFunction = (args) => rootAuthLoader(args);
export const ErrorBoundary = ClerkErrorBoundary();
import stylesheet from "~/tailwind.css?url";
export const links: LinksFunction = () => [
{ rel: "stylesheet", href: stylesheet },
];
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 className="max-w-[1000px] mx-auto">
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
function App() {
return (
<>
+ <Header />
<Outlet />
</>
);
}
export default ClerkApp(App);
Discussion