5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

HRBrainAdvent Calendar 2024

Day 19

React Router v7がリリースされたので試してみた

Last updated at Posted at 2024-12-19

HRBrain Advent Calendar 2024 19日目の記事です。

はじめに

こんにちは。yk_engrです。
普段はバックエンドエンジニアをしていますが、
過去にRemix v2を軽く触ったことがあり、今回、React Router v7がReact RouterとRemixの機能を統合する形でリリースされたため、何が変わったのかを一通り触ってみようと思いました。
remix_to_react_router.png

記事作成時のスタック

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ファイルに以下のような構成を書きます。

app/route.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 />内に、子ルートを配置し、レンダリングします。

layouts/bases/layout.tsx
export default function Layout() {
  return (
    <>
      <div>基本のレイアウト</div>
      <Outlet />
    </>
   );
}

ファイルベースルーティング

@react-route/fs-routesライブラリを使えば、
Remix v2のようなファイルベースのルーティングも可能です。

ローダー(データの読み込み)

データの読み込み時には、loaderclientLoaderを使用します。
loaderはサーバー側の初回レンダリング時にデータを取得し、clientLoaderは後続のクライアント側のナビゲーションで使用します。

routes/users/profile.tsx
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>
    </>
  );
}

アクション(データの変更)

データの変更には、actionclientActionを使用します。
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>タグを使うか、useSubmitfetcher.submitを使って、POSTメソッドが呼ばれた時に実行されます。

ナビゲーション

<Link><NavLink><Form>redirectuseNavigateを使用して、移動できます。
以下は、NavLinkコンポーネントの例です。

export function MyAppNav() {
  return (
    <nav>
      <NavLink to="/" end>ホーム</NavLink>
      <NavLink to="/about" end>About</NavLink>
      <NavLink to="/profile" end>プロフィール</NavLink>
    </nav>
  )
}

redirect

loaderaction内でリダイレクトする場合は、以下のように書きます。

export async function loader({ request }) {
  let profile = await getUserProfile(request);
  if (!user) {
    return redirect("/login");
  }
  return { name: profile.name };
}

まとめ

過去にRemix v2を触った経験があったため、ローダーやアクションなどの実装の部分はスムーズに理解できましたが、一方で、フロントエンドの実装経験が少なかったこともあり、動かすのに少し時間がかかりました。今後、フロントエンドの実装を検討する際に、どのフレームワークやルーティング方法を導入するかの参考になればと思います。

参考リンク

PR

株式会社HRBrainでは新しいメンバーを募集中です。
興味がある方は下記のリンクからよろしくお願いいたします。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?