気が付いたら Remix が React Router v7 に統合されてました。
ということで早速使ってみよう!👈😺ヨシッ!
React Router v7 のインストール
インストールはドキュメントに書かれてますので、適当なディレクトリでインストール!
npx create-react-router@latest my-react-router-app
Gitを初期化する?と聞かれるのでYesを選択。
npmで依存関係をインストールする?と聞かれるのでYesを選択。
完了したらcd
でプロジェクトに移動してnpm run dev
をします!
http://localhost:5173/ を開くと無事、React Router が起動しました!👍🏻👍🏻イェイ!
ルーティング(Routing)をお勉強する
ルーティングとは、超簡単に言うと『ホームページのページを決めてるルール』にあたります。
./
にアクセスしたら https://ydk.vc/ を表示./ccgridmap/
にアクセスしたら https://ydk.vc/ccgridmap/ を表示./category/programming/
にアクセスしたら https://ydk.vc/category/programming/ を表示
つまり、ルーティングを理解しないとページが作れないという訳です。重要だね。
ルーティングにちて、詳しい事はドキュメントにあります。
と言いたいのですが、ド素人にはちんぷんかんぷんでした。ので、1つ1つチェックしましょう。
インストール直後
ルーティングが設定されているのはmy-react-router-app/app/routes.ts
のファイルになります。
import { type RouteConfig, index } from "@react-router/dev/routes";
export default [index("routes/home.tsx")] satisfies RouteConfig;
ここでは http://localhost:5173/(つまりHTMLにおけるindex.html
が表示させる状態)を開いたらroutes/home.tsx
を表示させる、ということが設定されています。
試しにこの設定を変えてみましょう。
index() について
index()がインデックスの設定になっていそうなので、弄ってみます。
まずはmy-react-router-app/app/home/home.tsx
を作成してみます。
export default function Component() {
return (
<div className="h-screen flex items-center justify-center">
<div className="p-6 border border-gray-200 dark:border-gray-700 rounded-3xl">
New Home
</div>
</div>
);
}
続いて./app/routes.ts
を書き換えてみます。
import { type RouteConfig, index } from "@react-router/dev/routes";
export default [
//index("routes/home.tsx")
index("home/home.tsx"),
] satisfies RouteConfig;
保存してみると、無事さきほど作成したページが http://localhost:5173/ に表示されました!
でも、タイトルが消えてしまいましたので./app/home/home.tsx
を追加調整します。
import type { Route } from "./+types/home";
export function meta({}: Route.MetaArgs) {
return [
{ title: "New Home" },
{ name: "description", content: "Welcome to React Router!" },
];
}
export default function Component() {
return (
<div className="h-screen flex items-center justify-center">
<div className="p-6 border border-gray-200 dark:border-gray-700 rounded-3xl">
New Home
</div>
</div>
);
}
これでタイトルがちゃんと出ましたね!
route() について
今度は別のページを作ってみましょう。
./app/about/home.tsx
を作成します。
import type { Route } from "./+types/home";
export function meta({}: Route.MetaArgs) {
return [
{ title: "About" },
{ name: "description", content: "Welcome to React Router!" },
];
}
export default function Component() {
return (
<div className="h-screen flex items-center justify-center">
<div className="p-6 border border-gray-200 dark:border-gray-700 rounded-3xl">
About
</div>
</div>
);
}
続いて./app/routes.ts
を書き換えてみます。1行目のimport
にroute
が増えてるので注意!
import { type RouteConfig, index, route } from "@react-router/dev/routes";
export default [
//index("routes/home.tsx")
index("home/home.tsx"),
route("about", "about/home.tsx"),
] satisfies RouteConfig;
保存してみると、http://localhost:5173/about にさきほど作成したページが表示されました!
route() について(ルートモジュール)
route
はIDを入力されたら、そのIDに対応するページを表示させる。みたいなことができるみたいです。
route("teams/:teamId", "./teams/home.tsx"),
実際にやってみましょう。
./app/teams/home.tsx
を作成します。
import type { Route } from "./+types/home";
function fetchTeam(teamId: string): Promise<{ name: string }> {
return new Promise((resolve) => {
setTimeout(() => {
const name =
teamId === "tokyo"
? "東京チーム"
: teamId === "sapporo"
? "札幌チーム"
: teamId === "okinawa"
? "沖縄チーム"
: "なし";
resolve({ name: name });
}, 2000);
});
}
export async function loader({ params }: Route.LoaderArgs) {
let team = await fetchTeam(params.teamId);
return { name: team.name };
}
export function meta({ params }: Route.MetaArgs) {
const { teamId } = params;
return [
{ title: `${teamId} の検索結果` },
{ name: "description", content: "Welcome to React Router!" },
];
}
export default function Component({ loaderData }: Route.ComponentProps) {
return (
<div className="h-screen flex items-center justify-center">
<div className="p-6 border border-gray-200 dark:border-gray-700 rounded-3xl">
{loaderData.name}
</div>
</div>
);
}
そしてに./app/routes.ts
を書き換えてみます。
import { type RouteConfig, index, route } from "@react-router/dev/routes";
export default [
//index("routes/home.tsx")
index("home/home.tsx"),
route("about", "about/home.tsx"),
route("teams/:teamId", "./teams/home.tsx"),
] satisfies RouteConfig;
http://localhost:5173/teams/ を開くと404になりますが……
http://localhost:5173/teams/tokyo を開くとちゃんと出てきました!
折角なのでmeta
にloader
で取得した情報を受け渡しましょう。受け取るには{ params, matches }
を引数にすればOKです!!
export function meta({ params, matches }: Route.MetaArgs) {
const project = matches.find((match) => match && match.id === "teams/home")
const name = (project?.data as { name: string })?.name || "未設定";
return [
{ title: `${name} の検索結果` },
{ name: "description", content: "Welcome to React Router!" },
];
}
これを活用すれば、特定のIDでDBから情報を読み取ってきてOGPにサムネとか設定……みたいなこともできそうですね!便利~~!
route() について(入れ子)
なんとroute
は以下の様に入れ子にもできるそうです。テンプレートとして利用できるイメージですかね。
route("dashboard", "./dashboard/dashboard.tsx", [
index("./dashboard/home.tsx"),
route("settings", "./dashboard/settings.tsx"),
]),
実際にやってみましょう。
./app/dashboard/dashboard.tsx
を作成します。どうやら<Outlet />
に中身が挿入されるようですね。
import { Outlet } from "react-router";
export default function Dashboard() {
return (
<div className="h-screen flex items-center justify-center">
<div className="p-6 border border-gray-200 dark:border-gray-700 rounded-3xl">
<div className="space-y-6">
<h5 className="text-xl font-medium">
Dashboard
</h5>
<Outlet />
</div>
</div>
</div>
);
}
続いてインデックスとなる./app/dashboard/home.tsx
を作成します。
import type { Route } from "./+types/home";
export function meta({}: Route.MetaArgs) {
return [
{ title: "Dashboard" },
{ name: "description", content: "Welcome to React Router!" },
];
}
export default function Component() {
return (
<div>
これは Dashboard の Home です
</div>
);
}
続けて子のページとなる./app/dashboard/setting.tsx
を作成します。
import type { Route } from "./+types/settings";
export function meta({}: Route.MetaArgs) {
return [
{ title: "Dashboard - Setting" },
{ name: "description", content: "Welcome to React Router!" },
];
}
export default function Component() {
return (
<div>
これは Dashboard の Setting です
</div>
);
}
最後に./app/routes.ts
を書き換えてみます。
import { type RouteConfig, index, route } from "@react-router/dev/routes";
export default [
//index("routes/home.tsx")
index("home/home.tsx"),
route("about", "about/home.tsx"),
route("teams/:teamId", "./teams/home.tsx"),
route("dashboard", "./dashboard/dashboard.tsx", [
index("./dashboard/home.tsx"),
route("settings", "./dashboard/settings.tsx"),
]),
] satisfies RouteConfig;
http://localhost:5173/dashboard にはhomeが表示されますね!
そして http://localhost:5173/dashboard/settings にはsettingsがちゃんと出ました!
route() について(ルートモジュール&入れ子)
これ合わせ技できそうじゃない?と思って試して見た。
枠となる./app/message/message.tsx
を作成
import { Outlet } from "react-router";
export default function Dashboard() {
return (
<div className="h-screen flex items-center justify-center">
<div className="p-6 border border-gray-200 dark:border-gray-700 rounded-3xl">
<div className="space-y-6">
<h5 className="text-xl font-medium">
Message
</h5>
<Outlet />
</div>
</div>
</div>
);
}
indexとなる./app/message/home.tsx
を作成
import type { Route } from "./+types/home";
export function meta({}: Route.MetaArgs) {
return [
{ title: "Message" },
{ name: "description", content: "Welcome to React Router!" },
];
}
export default function Component() {
return (
<div>
これは Message の Home です
</div>
);
}
検索結果のページとなる./app/message/search.tsx
を作成
import type { Route } from "./+types/search";
export async function loader({ params }: Route.LoaderArgs) {
const { msgId } = params;
if (msgId == null) return { msg: "" };
const msg = msgId.split("").reverse().join("");
return { msg };
}
export function meta({ params }: Route.MetaArgs) {
const { msgId } = params;
return [
{ title: `${msgId} の検索結果` },
{ name: "description", content: "Welcome to React Router!" },
];
}
export default function Component({ loaderData }: Route.ComponentProps) {
return <div>{loaderData.msg}</div>;
}
最後に./app/routes.ts
を書き換える。
import { type RouteConfig, index, route } from "@react-router/dev/routes";
export default [
//index("routes/home.tsx")
index("home/home.tsx"),
route("about", "about/home.tsx"),
route("teams/:teamId", "./teams/home.tsx"),
route("dashboard", "./dashboard/dashboard.tsx", [
index("./dashboard/home.tsx"),
route("settings", "./dashboard/settings.tsx"),
]),
route("message", "./message/message.tsx", [
index("./message/home.tsx"),
route(":msgId", "./message/search.tsx"),
]),
] satisfies RouteConfig;
http://localhost:5173/message/ ではHomeが出てきた。
http://localhost:5173/message/Hello ではSearchが出てきた。完璧だ!
layout() について
これはroute
の入れ子と同じみたいですね。
違う点はroute
だとパターン(つまりパス)が必ず必要となるが、layout
ならパターンが不要なためどこでも使える、みたいな感じかな。たとえば./
にてテンプレートが欲しいときはlayout
を、それ以外ではroute
またはlayout
を利用する想定かな?
layout("./marketing/layout.tsx", [
index("./marketing/home.tsx"),
route("contact", "./marketing/contact.tsx"),
]),
route() について(ルートモジュール&入れ子)みたいな事もできそうですよね!
気付いたエラー(ミス)
モジュール ‘./+types/home’ またはそれに対応する型宣言が見つかりません。ts(2307)
型をインポートする際、1行目に
import type { Route } from "./+types/home";
って設定しますが、これって./app/routes.ts
にて該当のファイルを紐付けてないと
モジュール ‘./+types/home’ またはそれに対応する型宣言が見つかりません。ts(2307)
というエラーが表示されてしまうので、一瞬焦りますね……
‘loaderData’ は ‘undefined’ の可能性があります。ts(18048)
型のインポートをする際、本来は
import type { Route } from "./+types/home";
のように、そのファイルに合わせて型をインポートしなければならないのに、間違えて
import type { Route } from "../+types/root";
のようにルートなど別ファイルから、型をインポートすると
‘loaderData’ は ‘undefined’ の可能性があります。ts(18048)
という表示がされるので悩んだりもしてました。
〆
ザックリと使い方を理解出来て良かった!