👻

爆速開発!?React Router v7 フレームワークモードでルーティングが超進化!

に公開
1

フレームワークモードで刷新!🚀 新たな React Router

React Router のバージョン 7 が発表されましたね!🎉 Remix と力を合わせて「フレームワークモード」なんていう新しい機能が登場したり、ファイルベースルーティングが導入されたりと、かなり大きな変化がありました!😲

特に注目なのが、この「フレームワークモード」!💪 SSR(サーバーサイドレンダリング)、SPA(シングルページアプリケーション)、それにプレレンダリングっていう 3 つのいいとこ取りができちゃう、とってもパワフルな機能なんです ✨ 今回は、公式のチュートリアルコードを参考にしながら、この新しい React Router のフレームワークモードの魅力に迫っていきましょう!🚀

チュートリアルコードに学ぶ!

https://reactrouter.com/start/framework/installation#installation
まずは、公式チュートリアルの案内に従って create-react-router を実行してみましょう!コマンドをポチッと叩くだけで、あっという間にプロジェクトの雛形が完成しますよ!✨

% npx create-react-router@latest my-react-router-app
cd my-react-router-app
npm i
npm run dev

コマンドをいくつか実行するだけで、もう初期プロジェクトがブラウザで表示されちゃいました!👀 この手軽さ、すごくないですか!?😍

ファイル構成もシンプルで、app/routes/ ディレクトリにルーティング用のファイルを追加していくだけ!📝 面倒なエントリーポイントの設定ファイルが見当たらないのも、ルーティングに集中できて嬉しいポイントですよね 😊 HTML の <head> タグの中身(meta タグとか)も app/root.tsx で管理できちゃうみたい。ライブラリ側でよしなにやってくれるのは本当に助かります 🙏 詳しい仕組みは、また別の機会に探ってみるのも面白そう!🕵️‍♀️

構築ファイルも非常に少なく、サンプルページとして用意された welcome ページを除いてしまえば、構成ファイルはこんなものです。(とはいえ、いくらか端折りました)

.
app/
  |- routes/
    |- home.tsx          # Routeファイルの実体(このディレクトリ内に追加していく)
  |- root.tsx            # Routeファイルの基底となる設定が書かれている
  |- routes.tsx          # Routeファイルを束ねる役割
package.json
public/                  # 静的ファイルの置き場
react-router.config.ts   # 描画モード等の設定を記載する
vite.config.ts

vite.config.ts の中身も見てみます。

export default defineConfig({
  plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
});

reactRouter() のプラグインが入っています。
これによって、watch にあわせて typegen を実行させてもいるようです。

typegen

npm run dev で開発サーバーを起動すると、魔法のように .react-router/types っていうディレクトリがひょっこり現れます 🧙‍♀️ この中には、app/routes と同じ構造で型定義ファイル(.d.ts ってやつですね!)が自動で生成されるんです。すごい!✨

これが新機能「typegen」の力!💪 これのおかげで、URL の一部(パスパラメータって言います)とか、後で説明する loader っていう機能でページに渡されるデータの型とかを、React Router が賢く管理してくれるようになるんです。具体的には…

  • 👉️ ページごとに必要なデータを取ってきたり送ったりする処理(loader とか action って言います)が、 完全に型安全 になります!もうタイプミスで悩まない!😭
  • 👉️ ルート(ページのパス)の指定ミスなんかも、アプリをビルドするときに見つけてくれるようになります!安心感がすごい!😌

Tanstack Router などの後発ライブラリでの実装もあり、今回の実装に至ったのかと思います。(パスの横断管理はまだ難しそうだったので、今後に期待?)

個別に型生成を実行したい場合は、次のコマンドを使います。

npx react-router typegen

ちなみに、この型ファイルたちは app/routes ディレクトリの中身を元にして作られているみたい。だから、ファイルをリネームしたり移動したりしても、ちゃんと変更を検知して型ファイルを作り直してくれるんですよ。賢い!🐶

  • 👉️ ファイルの作成、移動を追従し、削除もおこなってくれる。
  • 👉️ .gitignore で除外されているため、git 管理しない作法である。

ファイル構成を変えてもちゃんとついてきてくれるなんて、開発が捗りますね!👍️

loader / clientLoader

さて、お次は loaderclientLoader です!✨ これらは、ページが表示される に、API を叩いてデータを取ってきたり、必要な準備をしてくれる超便利な機能なんです!💪
SSR(サーバーサイドレンダリング)みたいにサーバー側でページを作る場合は loader、SPA(シングルページアプリケーション)みたいにブラウザ側でページを組み立てる場合は clientLoader を使う、っていう風に使い分けるんですよ。賢いですよね!😉

デフォルトで用意されているページを少し書き換えて、API を実行してみました。

SSR レンダリングによる loarder 実行

// app/routes/home.tsx

import type { Route } from "./+types/home";
import { useLoaderData } from "react-router";

type Response = {
  name: string;
  description: string;
};

export function meta({}: Route.MetaArgs) {
  return [
    { title: "New React Router App" },
    { name: "description", content: "Welcome to React Router!" },
  ];
}

export async function loader({ request }: Route.LoaderArgs): Promise<Response> {
  const url = new URL(request.url);
  const apiUrl = new URL("/api.json", url).toString(); // publicディレクトリ下にJSONをおいただけです
  const response = await fetch(apiUrl);
  if (!response.ok) {
    throw new Response("Failed to fetch API data", { status: response.status });
  }
  const data = (await response.json()) as Response;
  return data;
}

export default function Home() {
  const data = useLoaderData() as { name: string; description: string };
  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.description}</p>
    </div>
  );
}

フレームワークモードだと、面倒な Provider の設定とかも不要で、本当に簡単にページ用のデータ取得処理(fetch)が書けちゃいました!🙌
特に SSR モードだと、ページが表示される時にはもうデータが用意されてるから、ローディング表示とかを待たずにパッと情報が見れて、ユーザー体験もすごく良くなりそう!💨

SPA での実行

config を書き換えると SPA でのレンダリングモードに切り替えができます。

// react-router.config.ts
export default {
  ssr: false,
} satisfies Config;

SPA では loader が実行できなくなるので、clientLoader に置き換えます。

- export async function loader({
+ export async function clientLoader({

SPA モードでも、Chrome の開発者ツールでちゃんと API が呼ばれてるのが確認できましたよ 👀 関数名を loader から clientLoader に変えるだけで、ちゃんと動いてくれるなんて、感動モノです!😭✨

action / clientAction (5/8 追記)

<完全に忘れてすっ飛ばしてました!>

お次は actionclientAction の出番だよ!🎬 loader がデータの GET 担当だとしたら、こっちは POST とか、他のメソッドでサーバーに何かお願いする時のトリガーになる機能なんだ。💪
早速、今度は <form /> を使って、実際にどう動くのか見てみよう!슝슝=3

SSR レンダリングによる action 実行

まずは SSR の場合から見ていこっか!サーバー側でどんな風に action が動くのか、ワクワクするね!🤩

// app/routes/home.tsx

import type { Route } from "./+types/home";
import { Form, useActionData } from "react-router";

type Action = {
  success: boolean;
  message: string;
  submittedData?: string;
};

export async function action({ request }: Route.ActionArgs): Promise<Action> {
  // フォームデータの取得
  const formData = await request.formData();
  const message = formData.get("message") as string;

  // モックのAPI処理
  console.log("送信されたデータ:", { message });

  // 成功レスポンスを返す
  return {
    success: true,
    message: "送信完了!",
    submittedData: message,
  };
}

export default function Home() {
  const actionData = useActionData() as Action | undefined;

  return (
    <div>
      <h2>メッセージを送信</h2>
      <Form method="post">
        <label>
          メッセージ:
          <input type="text" name="message" required />
        </label>
        <button type="submit">送信</button>
      </Form>

      {actionData && (
        <div>
          <div style={{ color: actionData.success ? "green" : "red" }}>
            {actionData.message}
          </div>
          {actionData.submittedData && (
            <div>送信内容: {actionData.submittedData}</div>
          )}
        </div>
      )}
    </div>
  );
}

フォームに何か入力して「送信」ってすると、action がバッチリ発火するよ!🔥
しかも、action から返ってきた結果は useActionData フックを使えば、コンポーネント側で簡単に受け取れちゃうんだ。便利すぎ!😍

見てみて!✨ React Hook Form みたいなライブラリを使わなくても、こんなにセマンティックで分かりやすい HTML が書けちゃうなんて、すごくない!?😳
もちろん、フォーム送信以外の方法で action を呼び出したい時もあるよね?そんな時は useFetcher っていう秘密兵器もあるから大丈夫!これを使えば、好きなタイミングでデータを送信したりできるんだ。例えばこんな感じ 👇

const fetcher = useFetcher();
fetcher.submit({ key: "value" }, { method: "post", action: "/some/path" });

これなら、実装で困ることはなさそうだね!😉👍️

総括

いや〜、新しい React Router、フレームワークモードのおかげで本当に できること が増えましたね!🚀
データの扱いが型安全になったり 🛡️、SSR や SPA、さらにはそれらを組み合わせたハイブリッドな構成も可能になったり 🌈、開発の自由度が格段にアップしました!✨

たしかに、新しく覚えることは少し増えたかもしれません 💦 でも、これ一つで色々できちゃうオールインワン感は魅力的!💖 これからもっと深く学んで、どんどん使いこなしていきたいですね!💪 みんなも一緒に React Router v7 の世界を探検しましょう!😁

Discussion