HRBrain Advent Calendar 2024 19日目の記事です。
はじめに
こんにちは。yk_engrです。
普段はバックエンドエンジニアをしていますが、
過去にRemix v2を軽く触ったことがあり、今回、React Router v7がReact RouterとRemixの機能を統合する形でリリースされたため、何が変わったのかを一通り触ってみようと思いました。
記事作成時のスタック
React Router v7
pnpm v9.12.2
参考
公式ドキュメントがベースですが、
ドキュメントの日本語版を作成してくださっている方がいたので、
こちらも参考にさせていただいてます。
ライブラリとしてのReact Router
以前のバージョンと同様に、シンプルで宣言的なルーティングライブラリとしても使用できますが、
今回は、フレームワークとしてのReact Routerを使用します。
インストール&起動
以下のようにcreateで作成して、run devで起動します。
$ pnpm create react-router@pre my-app
$ cd my-app
$ pnpm i
$ pnpm run dev
ブラウザでhttp://localhost:5173
を開けば起動していることが確認できます。
ルーティング
デフォルトではコンフィグベースでのルーティングで、app/routes.ts
ファイルに以下のような構成を書きます。
import { type RouteConfig, index, layout, prefix, route } from "@react-router/dev/routes";
export default [
// root.tsxの <Outlet /> を配置した箇所でレンダリングされる
index("routes/home.tsx"),
route("about", "routes/about.tsx"),
layout("layouts/auths/layout.tsx", [
// auths/layout.tsx内の <Outlet /> を配置した箇所でレンダリングされる
route("login", "routes/auths/login.tsx"),
route("logout", "routes/auths/logout.tsx"),
]),
layout("layouts/bases/layout.tsx", [
// bases/layout.tsx内の <Outlet /> を配置した箇所でレンダリングされる
route("profile", "routes/users/profile.tsx"),
]),
...prefix("todos", [
// root.tsxの <Outlet /> を配置した箇所でレンダリングされる
index("routes/todos/home.tsx"),
route("detail", "routes/todos/detail.tsx")
]),
] satisfies RouteConfig;
Remixと同様に、<Outlet />
内に、子ルートを配置し、レンダリングします。
export default function Layout() {
return (
<>
<div>基本のレイアウト</div>
<Outlet />
</>
);
}
ファイルベースルーティング
@react-route/fs-routes
ライブラリを使えば、
Remix v2のようなファイルベースのルーティングも可能です。
ローダー(データの読み込み)
データの読み込み時には、loader
とclientLoader
を使用します。
loader
はサーバー側の初回レンダリング時にデータを取得し、clientLoader
は後続のクライアント側のナビゲーションで使用します。
import type { Route } from "./+types/profile";
export function loader() {
return {
name: "田中 太郎",
gender: "男性",
birthday: "2000/05/03"
}
}
export default function Profile({ loaderData }: Route.ComponentProps) {
return (
<>
<div>名前: {loaderData.name}</div>
<div>性別: {loaderData.gender}</div>
<div>誕生日: {loaderData.birthday}</div>
</>
);
}
アクション(データの変更)
データの変更には、action
とclientAction
を使用します。
action
はサーバーでのみ呼び出され、clientLoader
はブラウザで実行されます。
export async function action({
request,
}: Route.ActionArgs) {
let formData = await request.formData();
let name = await formData.get("name");
let profile = await fakeDb.updateProfile({ name });
return project;
}
アクションの呼び出し
アクションは、<Form>
タグを使うか、useSubmit
やfetcher.submit
を使って、POSTメソッドが呼ばれた時に実行されます。
ナビゲーション
<Link>
、<NavLink>
、<Form>
、redirect
、useNavigate
を使用して、移動できます。
以下は、NavLinkコンポーネントの例です。
export function MyAppNav() {
return (
<nav>
<NavLink to="/" end>ホーム</NavLink>
<NavLink to="/about" end>About</NavLink>
<NavLink to="/profile" end>プロフィール</NavLink>
</nav>
)
}
redirect
loader
やaction
内でリダイレクトする場合は、以下のように書きます。
export async function loader({ request }) {
let profile = await getUserProfile(request);
if (!user) {
return redirect("/login");
}
return { name: profile.name };
}
まとめ
過去にRemix v2を触った経験があったため、ローダーやアクションなどの実装の部分はスムーズに理解できましたが、一方で、フロントエンドの実装経験が少なかったこともあり、動かすのに少し時間がかかりました。今後、フロントエンドの実装を検討する際に、どのフレームワークやルーティング方法を導入するかの参考になればと思います。
参考リンク
PR
株式会社HRBrainでは新しいメンバーを募集中です。
興味がある方は下記のリンクからよろしくお願いいたします。