型付きルーター
@lazarv/react-serverには完全に型付けされたルーティングソリューションが含まれています。すべてのルート、リンク、パラメータ、検索パラメータをコンパイル時に型チェックでき、ルートパス、パラメータ、検索値のIDEオートコンプリートが利用できます。手動で実行するコード生成ステップや外部ツールは不要です。
ルートディスクリプタはどこでも使えます — サーバーコンポーネント、クライアントコンポーネント、共有モジュール。同じcreateRoute呼び出しから、型付きLinkコンポーネント、型付きフック(useParams、useSearchParams)、型付きプログラムナビゲーション、そして任意のスキーマライブラリ(Zod、ArkType、Valibot、またはValidateSchemaインターフェースを満たすもの)や軽量パース関数によるオプションのランタイムバリデーションが得られます。
ファイルシステムベースのルーティングについては、これらのプリミティブを自動的に構築するファイルシステムベースのルーターを参照してください。低レベルのRouteコンポーネントAPIについては、以下のRouteコンポーネントを参照してください。
createRouteを使用して、完全な型安全性を持つルートディスクリプタを定義します。ルートディスクリプタはルートのパス、バリデーションルール、型付きヘルパーを保持し、サーバーコンポーネントとクライアントコンポーネントの両方からインポートできます。
routes.tsimport { createRoute } from "@lazarv/react-server/router";
// 静的ルート — パラメータなし
export const home = createRoute("/", { exact: true });
// 動的ルート — パターンからパラメータを抽出
export const user = createRoute("/user/[id]", { exact: true });
// キャッチオールルート — パラメータはstring[]
export const docs = createRoute("/docs/[...slug]");
// フォールバックルート — 他にマッチしない場合にマッチ
export const notFound = createRoute("*");
// スコープ付きフォールバック — /user/配下で他のルートが処理しないものにマッチ
export const userNotFound = createRoute("/user/*");
パスパターンがパラメータの型を自動的に決定します:
| パターン | 抽出される型 |
|---|---|
/user/[id] | { id: string } |
/blog/[slug]/[commentId] | { slug: string; commentId: string } |
/files/[...path] | { path: string[] } |
* | {} |
スコープ付きフォールバックは、他のルートが処理しない場合にプレフィックス配下のURLにのみマッチします。これはコンテキスト固有の404ページを表示するのに便利です — 例えば、ユーザーが見つからないページで「ユーザーを検索」UIを表示し、グローバルな*フォールバックでは汎用メッセージを表示できます。
サーバーサイドでは、ルートディスクリプタをReact要素にバインドし、ルーターに収集します。ルーターはマッチしたルートをレンダリングする<Routes />コンポーネントを提供します。ルーター定義をレイアウトから分離すると、各ファイルが単一の関心事に集中できます。
router.tsximport { createRoute, createRouter } from "@lazarv/react-server/router";
import * as routes from "./routes";
import Home from "./Home";
import UserPage from "./UserPage";
import NotFound from "./NotFound";
const router = createRouter({
home: createRoute(routes.home, <Home />),
user: createRoute(routes.user, <UserPage />),
notFound: createRoute(routes.notFound, <NotFound />),
});
export default router;
App.tsximport router from "./router";
export default function App() {
return (
<div>
<nav>
<router.home.Link>Home</router.home.Link>
<router.user.Link params={{ id: 42 }}>User 42</router.user.Link>
</nav>
<router.Routes />
</div>
);
}
createRoute(descriptor, element)オーバーロードは、既存のルートディスクリプタを受け取りReact要素にバインドし、.Routeコンポーネントを持つ完全なTypedRouteを生成します。createRouter関数はこれらをルーターオブジェクトに収集し、各ルートは名前でアクセスできます。
ルートディスクリプタにサーバーコンポーネントは不要です。クライアント上でのみ存在するルートを作成できます — タブ、モーダル状態、フィルター、またはサーバーラウンドトリップなしでURLに基づいてUIを変更する場合に便利です。
パスとオプションだけでcreateRouteを呼び出すと(要素なし)、.Link、.href()、.useParams()、.useSearchParams()を持つルートディスクリプタが得られます。これらのディスクリプタを"use client"コンポーネントで使用すると、ルーティングは完全にクライアント上で行われます。
routes.tsimport { createRoute } from "@lazarv/react-server/router";
import { z } from "zod";
export const settings = createRoute("/settings/[tab]", {
exact: true,
validate: {
params: z.object({
tab: z.enum(["profile", "security", "billing"]),
}),
},
});
SettingsPage.tsx"use client";
import { settings } from "./routes";
export default function SettingsPage() {
const params = settings.useParams();
const tab = params?.tab ?? "profile";
return (
<div>
<nav>
<settings.Link params={{ tab: "profile" }}>Profile</settings.Link>
<settings.Link params={{ tab: "security" }}>Security</settings.Link>
<settings.Link params={{ tab: "billing" }}>Billing</settings.Link>
</nav>
{tab === "profile" && <ProfileForm />}
{tab === "security" && <SecuritySettings />}
{tab === "billing" && <BillingInfo />}
</div>
);
}
ファイルシステムベースのルーターでは、先頭に"use client"を持つページは自動的にクライアント専用ルートになります。ディレクティブとデフォルトエクスポートを持つページファイルを作成するだけです:
pages/counter.tsx"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}
pages/clock.tsx"use client";
import { useState, useEffect } from "react";
export default function Clock() {
const [time, setTime] = useState(new Date());
useEffect(() => {
const id = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(id);
}, []);
return <p>Current time: {time.toLocaleTimeString()}</p>;
}
これらのページはクライアントルートストアに自動的に登録されます。これらの間のナビゲーションは完全にクライアント上で行われ — サーバーラウンドトリップなしで、ReactのActivityコンポーネントによるインスタントな表示/非表示トランジションでコンポーネントの状態が保持されます。
ルートがクライアントコンポーネントとして登録されている場合 — ファイルシステムルーター、<Route>コンポーネント、createRouterのいずれを通じても — ランタイムはクライアントサイドのルートストアで追跡します。ターゲットURLのすべてのルートがクライアント専用の場合、ナビゲーションは完全にクライアント上で行われ — サーバーリクエストは不要です。これにより、クライアント専用ルートはタブ、カウンター、時計、またはナビゲーションを超えて存続すべきステートフルUIなどの高速でインタラクティブなUIパターンに最適です。
クライアント専用ルートとサーバールートを自由に混在させることができます。プレーンな.tsファイルで共有ルートディスクリプタを定義し、両方の環境でインポートします:
routes.tsimport { createRoute } from "@lazarv/react-server/router";
// サーバーとクライアントの両方で動作 — ディスクリプタのみ(要素なし)
export const home = createRoute("/");
export const user = createRoute("/user/[id]");
サーバーコンポーネントからインポートすると、useParams()はHTTPリクエストコンテキストから読み取ります。クライアントコンポーネントからインポートすると、ブラウザのロケーションから読み取ります。ランタイムはViteエイリアスを介して正しい実装を解決するため、どこでも同じコードを書くことができます。
すべてのルートディスクリプタには、コンパイル時に正しいパラメータと検索型を強制する.Linkコンポーネントがあります。動的セグメントのないルートはparamsを受け付けません。動的セグメントを持つルートはparamsが必須です。
// パラメータなし — paramsを渡すとTypeScriptエラー
<home.Link>Home</home.Link>
// 必須パラメータ — paramsを省略するとTypeScriptエラー
<user.Link params={{ id: 42 }}>User 42</user.Link>
// 検索パラメータ付き
<post.Link params={{ slug: "hello" }} search={{ tab: "comments" }}>
Post
</post.Link>
// すべての標準Linkプロップ(prefetch、rollbackなど)がサポートされます
<user.Link params={{ id: 1 }} prefetch>User 1</user.Link>
フォールバックルート(*)にはアドレス指定可能なパスがないため、.Linkコンポーネントはありません。
ルートディスクリプタは、型付きでバリデーション済みのパラメータと検索パラメータを返すフックを提供します。これらのフックはサーバーコンポーネント(RSC)とクライアントコンポーネントの両方で動作します。
UserPage.tsximport { user } from "./routes";
export default function UserPage() {
// ルートがマッチしない場合は型付きパラメータまたはnullを返す
const params = user.useParams();
// params: { id: string } | null
if (!params) return <p>No match</p>;
return <h2>User {params.id}</h2>;
}
ProductList.tsx"use client";
import { products } from "./routes";
export default function ProductList() {
// デフォルト付きのバリデーション済み検索パラメータを返す
const { sort, page } = products.useSearchParams();
// sort: "name" | "price" | "rating", page: number
return <div>Sorted by {sort}, page {page}</div>;
}
サーバーでは、useParams()はHTTPリクエストコンテキストからパラメータを読み取ります。クライアントでは、ブラウザのロケーションから読み取ります。同じルートディスクリプタが両方の環境で動作します — ランタイムがViteエイリアスを介して正しい実装を解決します。
各ルートディスクリプタには、型付きパラメータからURLパス名を構築するhref()メソッドがあります:
user.href({ id: 42 }); // → "/user/42"
post.href({ slug: "hello" }); // → "/post/hello"
home.href(); // → "/"
これはJSXの外でURLを生成するのに便利です — サーバー関数、APIハンドラー、リダイレクト、メタデータなどで。
@lazarv/react-server/navigationのuseNavigateフックは、型安全なプログラムナビゲーションのためにルートディスクリプタを受け付けます:
"use client";
import { useNavigate } from "@lazarv/react-server/navigation";
import { user, products } from "./routes";
export default function SearchButton() {
const navigate = useNavigate();
return (
<button onClick={() => navigate(user, { params: { id: 42 } })}>
Go to User 42
</button>
);
}
型付きルートにナビゲーションする場合、検索パラメータは現在のURL検索パラメータにマージされます — 変更したいパラメータのみを指定します。
ルートパラメータと検索パラメータは、ValidateSchemaインターフェースを満たす任意のスキーマライブラリを使用してランタイムでバリデーションできます。ランタイムは3つのバリデーション戦略を順に試行します:
.safeParse(data)— Zod、Valibot、および互換ライブラリ.assert(data)— ArkType(失敗時にスロー、成功時にTを返す).parse(data)— 汎用フォールバック(失敗時にスロー)
// これらの形状のいずれかがValidateSchema<T>を満たします:
// Zod / Valibotスタイル
interface SafeParseSchema<T> {
parse(data: unknown): T;
safeParse(data: unknown): { success: true; data: T } | { success: false; error: unknown };
}
// ArkTypeスタイル
interface AssertSchema<T> {
assert(data: unknown): T;
}
// 汎用パーススタイル
interface ParseSchema<T> {
parse(data: unknown): T;
}
TypeScriptはスキーマの出力型からパラメータと検索型を推論します。例えば、z.object({ id: z.coerce.number() })を渡すと、TypeScriptは.parse()が{ id: number }を返すことを認識し、その型をuseParams()、.Link、.href()に流します。これは構造的型付けです — 特定のライブラリへの依存はありません。
Zodの場合:
routes.tsimport { createRoute } from "@lazarv/react-server/router";
import { z } from "zod";
export const user = createRoute("/user/[id]", {
exact: true,
validate: {
params: z.object({
id: z.coerce.number().int().positive(),
}),
},
});
export const products = createRoute("/products", {
exact: true,
validate: {
search: z.object({
sort: z.enum(["name", "price", "rating"]).catch("name"),
page: z.coerce.number().int().positive().catch(1),
}),
},
});
ArkTypeの場合:
routes.tsimport { createRoute } from "@lazarv/react-server/router";
import { type } from "arktype";
export const user = createRoute("/user/[id]", {
exact: true,
validate: {
params: type({ id: "string.numeric.parse" }),
},
});
export const products = createRoute("/products", {
exact: true,
validate: {
search: type({
"sort?": "'name' | 'price' | 'rating'",
"page?": "string.numeric.parse",
}),
},
});
この設定では:
/user/42→user.useParams()は{ id: 42 }を返す(数値に変換)/user/abc→user.useParams()はnullを返す(バリデーション失敗)/products?sort=price&page=2→products.useSearchParams()は{ sort: "price", page: 2 }を返す/products?sort=invalid→products.useSearchParams()は{}を返す(バリデーション失敗、フォールバックを返す)
バリデーション済みの型はフックと.Linkコンポーネントに流れます — IDEは生の文字列ではなくバリデーション済みの型を表示します。
完全なスキーマライブラリが不要な場合は、parseを使用して軽量な型変換とバリデーションを行います。各キーは生の文字列を目的の型に変換する関数にマップします。
routes.tsimport { createRoute } from "@lazarv/react-server/router";
export const post = createRoute("/post/[slug]", {
exact: true,
parse: {
params: { slug: String },
search: {
tab: (v) =>
["content", "comments", "related"].includes(v) ? v : "content",
q: String,
},
},
});
Number、Boolean、String、Dateなどの組み込みコンストラクタはパース関数として直接使用できます。カスタム関数で制約を強制できます — 上の例では、tabは許可リストに対してバリデーションし、不明な値には"content"にフォールバックします。
パース関数は次の場合に適しています:
- 依存関係を追加せずにシンプルな型変換を行いたい場合
- 無効な値に対する軽量なフォールバックが必要な場合
- バリデーションロジックが単純な場合
複雑なスキーマ、ネストされたオブジェクト、詳細なエラーメッセージが必要な場合は、スキーマライブラリとvalidateを使用してください。
型付きLinkコンポーネントとuseNavigateの両方が検索パラメータの関数型アップデーターをサポートしています。静的オブジェクトの代わりに、現在の検索パラメータを受け取り新しい値を返す関数を渡します:
// Linkで関数型アップデーター
<products.Link search={(prev) => ({ ...prev, page: prev.page + 1 })}>
Next Page
</products.Link>
// useNavigateで関数型アップデーター
navigate(products, {
search: (prev) => ({ ...prev, sort: "price", page: 1 }),
});
関数型アップデーターはクリック時に現在のパラメータを読み取るため、複数の更新が連続して発生する場合のステールクロージャバグを回避します。また、完全な現在の状態を事前に知る必要なく、一つのパラメータを変更しながら残りを保持するデルタ更新が容易になります。
SearchParamsコンポーネントは、URLとアプリケーション間の双方向変換レイヤーを提供します。コンパクトなURL形式を構造化パラメータにデコードしたり、コンポーネントに渡す前にトラッキングパラメータを除去するために使用します。
各ルートディスクリプタには、そのルートがマッチした場合にのみアクティブになるルートスコープのSearchParamsコンポーネントがあります:
StripTrackingParams.tsx"use client";
import { products } from "./routes";
// URLには?price=min-maxを保存するが、アプリには?min_price & ?max_priceを公開
function decode(sp) {
const range = sp.get("price");
if (!range) return sp;
const [min, max] = range.split("-");
const result = new URLSearchParams(sp);
result.delete("price");
result.set("min_price", min);
result.set("max_price", max);
return result;
}
function encode(sp) {
const min = sp.get("min_price");
const max = sp.get("max_price");
if (min == null && max == null) return sp;
const result = new URLSearchParams(sp);
result.delete("min_price");
result.delete("max_price");
result.set("price", `${min ?? 0}-${max ?? 10000}`);
return result;
}
export default function ProductPriceRange({ children }) {
return (
<products.SearchParams decode={decode} encode={encode}>
{children}
</products.SearchParams>
);
}
- decode — 読み取りをインターセプト:フックとバリデーションが参照する前に生のURLパラメータを変換
- encode — 書き込みをインターセプト:URLに入る前にマージされたパラメータを変換
ネストがサポートされています — decodeは外→内にチェーンされ、encodeは内→外にチェーンされます。ルーターはcreateRouterを通じてすべてのルートに適用されるグローバルSearchParamsコンポーネントも公開します。
スキーマバリデーション、軽量パース、createRouterを使用した完全な型付きルーターのセットアップ例:
routes.tsimport { createRoute } from "@lazarv/react-server/router";
import { z } from "zod";
export const home = createRoute("/", { exact: true });
export const about = createRoute("/about", { exact: true });
export const user = createRoute("/user/[id]", {
exact: true,
validate: {
params: z.object({ id: z.coerce.number().int().positive() }),
},
});
export const post = createRoute("/post/[slug]", {
exact: true,
parse: {
params: { slug: String },
search: {
tab: (v) =>
["content", "comments", "related"].includes(v) ? v : "content",
q: String,
},
},
});
export const notFound = createRoute("*");
router.tsximport { createRoute, createRouter } from "@lazarv/react-server/router";
import * as routes from "./routes";
import Home from "./Home";
import About from "./About";
import UserPage from "./UserPage";
import PostPage from "./PostPage";
import NotFound from "./NotFound";
const router = createRouter({
home: createRoute(routes.home, <Home />),
about: createRoute(routes.about, <About />),
user: createRoute(routes.user, <UserPage />),
post: createRoute(routes.post, <PostPage />),
notFound: createRoute(routes.notFound, <NotFound />),
});
export default router;
App.tsximport router from "./router";
export default function App() {
return (
<div>
<nav>
<router.home.Link>Home</router.home.Link>
<router.about.Link>About</router.about.Link>
<router.user.Link params={{ id: 42 }}>User 42</router.user.Link>
<router.post.Link
params={{ slug: "hello-world" }}
search={{ tab: "comments" }}
>
Hello World
</router.post.Link>
</nav>
<router.Routes />
</div>
);
}
注意: 完全な動作例については、リポジトリ内のtyped-routerとtyped-file-routerのサンプルをチェックしてください。
すべての型付きルーターエクスポートは@lazarv/react-server/routerから利用できます。
ルートディスクリプタまたは完全な型付きルートを作成します。返される型は引数によって異なります:
ルートディスクリプタ(要素なし) — RouteDescriptorを返します。.Link、.href()、.useParams()、.useSearchParams()、.SearchParamsを持ちますが、.Routeはありません。サーバーとクライアントの両方のコンポーネントで安全にインポートできます。
createRoute(path, options?) // → RouteDescriptor
createRoute("*", options?) // → RouteDescriptor(フォールバック)
createRoute("/prefix/*", options?) // → RouteDescriptor(スコープ付きフォールバック)
型付きルート(要素あり) — RouteDescriptorを拡張し.Routeコンポーネントを追加したTypedRouteを返します。サーバーエントリーポイントでReact要素をルートにバインドするために使用します。
createRoute(path, element, options?) // → TypedRoute
createRoute("*", element, options?) // → TypedRoute(フォールバック)
createRoute("/prefix/*", element, options?) // → TypedRoute(スコープ付きフォールバック)
createRoute(element, options?) // → TypedRoute(グローバルフォールバック)
ディスクリプタから — 既存のRouteDescriptorを受け取り要素にバインドし、TypedRouteを生成します。これは推奨パターンです:共有ファイルでディスクリプタを定義し、サーバーエントリーポイントで要素をバインドします。
createRoute(descriptor, element) // → TypedRoute
オプション:
| オプション | 型 | 説明 |
|---|---|---|
exact | boolean | プレフィックスマッチではなく完全パスマッチ。デフォルト:false |
validate | { params?, search? } | ランタイムバリデーション用のスキーマオブジェクト。ValidateSchema<T>インターフェースを満たす任意のライブラリ(Zod、ArkType、Valibotなど)が使用可能 — TypeScriptがスキーマの出力型からパラメータ/検索型を推論 |
parse | { params?, search? } | 軽量パーサー関数マップ。各キーが生の文字列を目的の型にマップ |
loading | ReactNode | ComponentType | ルートのロード中に表示されるSuspenseフォールバック |
matchers | Record<string, (value: string) => boolean> | [param=matcher]構文のカスタムマッチャー |
render | (params) => ReactNode | パラメータとchildrenを受け取るレイアウト関数 |
children | ReactNode | ルート内にレンダリングされるネストコンテンツ |
validateとparseは相互排他的な戦略です。互換ライブラリによるスキーマベースのバリデーションにはvalidateを、軽量な関数ベースの変換にはparseを使用してください。
createRoute(path, options?)で返されるオブジェクト — 要素がバインドされていないルート。クライアントコンポーネントや共有ルート定義ファイルで使用する形状です。
| プロパティ / メソッド | 型 | 説明 |
|---|---|---|
path | string | undefined | ルートパスパターン |
exact | boolean | 完全一致が必要かどうか |
fallback | boolean | フォールバックルートかどうか |
validate | RouteValidate | null | バリデーションスキーマ(提供された場合) |
parse | RouteParse | null | パース関数(提供された場合) |
Link | React.FC<...> | 型付きLinkコンポーネント。パスに動的セグメントがある場合はparamsが必須。searchをオブジェクトまたは関数型アップデーターとして受け付ける。フォールバックルートでは利用不可 |
href(params?) | (params?) => string | 型付きパラメータからURLパス名を構築。フォールバックルートでは利用不可 |
useParams() | () => TParams | null | 型付きパラメータを返すフック。ルートがマッチしないかバリデーションが失敗した場合はnull。サーバーとクライアントの両方のコンポーネントで動作 |
useSearchParams() | () => TSearch | 型付き検索パラメータを返すフック。設定されている場合はバリデーションまたはパースを適用 |
SearchParams | React.FC<SearchParamsProps> | ルートスコープの検索パラメータ変換境界。このルートがマッチした場合にのみアクティブ |
RouteDescriptorを.Routeコンポーネントで拡張。要素が提供された場合にcreateRouteから返されます。
| プロパティ | 型 | 説明 |
|---|---|---|
| ...すべてのRouteDescriptorプロパティ | ||
Route | React.FC<...> | バインドされた要素をレンダリングするルートコンポーネント。オプションのオーバーライドを受け付ける:element、loading、render、children、exact、fallback |
型付きルートをルーターオブジェクトに収集します。すべてのルートを名前で展開し、RoutesとSearchParamsコンポーネントを追加したTypedRouterを返します。
const router = createRouter({
home: createRoute(routes.home, <Home />),
user: createRoute(routes.user, <UserPage />),
notFound: createRoute(routes.notFound, <NotFound />),
});
TypedRouterプロパティ:
| プロパティ | 型 | 説明 |
|---|---|---|
Routes | React.FC | すべてのルートを宣言順にレンダリング。ルートコンテンツを表示したい場所に配置 |
SearchParams | React.FC<SearchParamsProps> | グローバル検索パラメータ変換境界 — すべてのルートに適用 |
[routeName] | TypedRoute | 各ルートはcreateRouterに渡したキーでアクセス可能。router.user.Link、router.user.useParams()など |
双方向の検索パラメータ変換境界。スタンドアロンコンポーネント、各ルートディスクリプタ、ルーターオブジェクトとして利用可能です。
import { SearchParams } from "@lazarv/react-server/router";
// スタンドアロン — 内部のすべてのルートに適用
<SearchParams decode={decode} encode={encode}>
{children}
</SearchParams>
// ルートスコープ — このルートがマッチした場合のみ適用
<products.SearchParams decode={decode} encode={encode}>
{children}
</products.SearchParams>
// ルーターレベル — ルーター内のすべてのルートに適用
<router.SearchParams decode={decode} encode={encode}>
{children}
</router.SearchParams>
プロップ:
| プロップ | 型 | 説明 |
|---|---|---|
decode | (sp: URLSearchParams) => URLSearchParams | フックとバリデーションが読み取る前にURLパラメータを変換 |
encode | (sp: URLSearchParams, current: URLSearchParams) => URLSearchParams | URLに書き込まれる前にマージされたパラメータを変換 |
children | ReactNode |
低レベルのRouteコンポーネントはJSXによる基本的なパスマッチングを提供します。シンプルなユースケースや、型付きディスクリプタなしでルートレンダリングを直接制御したい場合に便利です。
import { Route } from '@lazarv/react-server/router';
export default function App() {
return (
<Route path="/about">
<About />
</Route>
<Route path="/readme" element={<Readme />} />
);
}
ルートを定義すると、定義したパスで始まるすべてのパスにマッチします。完全一致にするにはexactプロップを使用します:
<Route path="/about" exact>
<About />
</Route>
ルートのネスト、renderプロップによるレイアウトの定義、ルートパラメータの使用ができます:
import { Route } from '@lazarv/react-server/router';
function Layout({ children }) {
return (
<div>
<h1>Layout</h1>
{children}
</div>
);
}
function User({ id }) {
return <h2>User {id}</h2>;
}
export default function App() {
return (
<Route path="/" render={Layout}>
<Route path="/" exact element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users/[id]" render={User} />
<Route path="/files/[...path]" render={File} />
<Route fallback element={<NotFound />} />
</Route>
);
}
ルートパラメータはcreateRouteと同じブラケット構文を使用します:[id]は単一パラメータ、[...path]はキャッチオールパラメータ。カスタムマッチャーも定義できます:
const matchers = {
number: (value) => /^\d+$/.test(value),
};
<Route path="/files/[id=number]" render={File} matchers={matchers} />
フォールバックルートは他のルートがマッチしない場合にマッチします。path="*"またはfallbackプロップを使用します:
<Route path="/about" element={<About />} />
<Route fallback element={<NotFound />} />
より複雑なアプリケーションには、代わりに
createRouteとcreateRouterを使用してください — 型安全性、バリデーション、より良い開発者体験を提供します。Routeコンポーネントはクイックプロトタイプやシンプルなアプリに最適です。
コンポーネント内でredirect関数を使用することで、他の場所にリダイレクトできます。より正確には、サーバーサイドレンダリング中の任意の場所でredirectを使用できますが、RedirectErrorがスローされます。ランタイムはこのエラーをキャッチして、クライアントにリダイレクト応答を送信します。
import { redirect } from "@lazarv/react-server";
export default function App() {
return redirect("/user");
}
kindパラメータを指定して、クライアントでのリダイレクトの動作を制御することもできます:
import { redirect } from "@lazarv/react-server";
export default function ProtectedPage() {
// pushStateでリダイレクト(ユーザーが戻れるように)
redirect("/login", 302, "push");
}
利用可能なリダイレクト種類:"navigate"(デフォルト、replaceState)、"push"(pushState)、"location"(完全なブラウザナビゲーション)、"error"(カスタム処理のためにクライアントでスロー)。詳細はHTTPリダイレクトのドキュメントを参照してください。
現在のリクエストのURL内のパス名を変更するためにrewrite関数を使用できます。これはクライアントをリダイレクトせずにURLを変更したい場合に便利です。ミドルウェアで使用するのに最適です。
import { rewrite } from "@lazarv/react-server";
export default function App() {
return rewrite("/user");
}
サーバー関数では、reload関数を使って現在のページやアウトレットをリロードできます。これはミューテーション後にアプリの要素をリフレッシュするためにページやアウトレットをリロードしたい場合に便利です。
"use server";
import { reload } from "@lazarv/react-server";
export async function addTodo(todo) {
await addTodo(todo);
reload();
}
reload関数にURLとアウトレット名を渡して、別のルートとアウトレットをレンダリングすることもできます。この方法を使えば、サーバー関数を使ってデータを変更する場合でも、アプリ全体の不要な再レンダリングを回避してパフォーマンスを最適化できます。
"use server";
import { reload } from "@lazarv/react-server";
export async function addTodo(todo) {
await addTodo(todo);
reload("/todos", "todo-list");
}
ミドルウェアはルートハンドラの前に実行される関数です。認証、ロギング、解析など、さまざまなことに使用できます。
エントリモジュールからinit$という名前のasync関数をエクスポートしてください。この関数はミドルウェアランナーとなるリクエストハンドラを初期化します。init$関数はasync関数を返す必要があります。この関数がミドルウェアランナーとなります。
// index.jsx
export async function init$() {
return async (context) {
// do something
};
}
@lazarv/react-serverモジュールから利用可能なすべての関数をミドルウェアで使用できます。リクエストをリダイレクトしたり書き換えたり、クッキーを管理したり、レスポンスにヘッダーを追加したりできます。React Server Componentで使える機能は、ミドルウェアでもすべて利用可能です。