TL;DR
React のルーティングライブラリ。Remix と同じチームが開発しており、Remix の内部でも使われている。createBrowserRouter でルートを定義すると、データの取得と更新をルートに結び付ける Data APIs が使える。
仕組み
ルートの定義
従来の BrowserRouter ではなく createBrowserRouter でルートを定義すると、Data APIs が使えるようになる。
npm install react-router react-router-domconst router = createBrowserRouter([
{ path: "/signin", element: <GuestRoute children={<SignIn />} /> },
{ path: "/signup", element: <GuestRoute children={<SignUp />} /> },
{ path: "", element: <PrivateRoute children={<Main />} /> },
{ path: "*", element: <NotFound /> },
])
function App() {
return <RouterProvider router={router} />
}Data APIs
Data APIs は、データの取得と更新を効率よく行うための API である。ルーティングとデータ管理が密に統合される。
Loader は、ルートが描画される前にデータを取得する非同期関数である。ルート定義の loader に設定し、取得したデータは useLoaderData で受け取る。
function loader() {
return fetchFakeAlbums()
}
const router = createBrowserRouter([{ path: "/", loader: loader, element: <Albums /> }])
export function Albums() {
const albums = useLoaderData()
// ...
}Action は、フォームの送信などに応じてデータを更新する非同期関数である。ルート定義の action に設定し、処理結果は useActionData で受け取る。フォームには <Form> コンポーネントを使う。method には <form> と違って PUT、PATCH、DELETE も指定できる。useSubmit を使うと、コードからフォームを送信できる。
{
path: "/users/new",
element: <UserForm />,
action: async ({ request }) => {
const formData = await request.formData();
const response = await fetch("/api/users", { method: "POST", body: formData });
return response.json();
},
}useFetcher は、特定のルートに依存せずにデータの取得や更新を行うフックである。検索フォームやいいねボタンのように、ページ遷移を伴わない操作に使う。fetcher.Form は <Form> と同じように使えるが、ページ遷移が起きない。<Form navigate={false}> でも同じことができる。ほかに fetcher.load、fetcher.submit、リクエストの状態を表す fetcher.state、戻り値を持つ fetcher.data がある。
function Search() {
const fetcher = useFetcher()
return (
<div>
<fetcher.Form action="/api/search" method="get">
<input name="query" type="text" />
<button type="submit">検索</button>
</fetcher.Form>
{fetcher.data && <SearchResults results={fetcher.data} />}
</div>
)
}Loader や Action の処理の状態は、useNavigation の state で取得できる。
エラーハンドリング
ルートの errorElement に Error Boundary を渡し、その中で useRouteError を使うと、ルートごとにエラーを扱える。loader や action、ルートのコンポーネントでエラーが起きたときに、指定したエラーページを表示する。
;<Route
path="/invoices/:id"
loader={loadInvoice}
action={updateInvoice}
element={<Invoice />}
errorElement={<ErrorBoundary />}
/>
function ErrorBoundary() {
let error = useRouteError()
console.error(error)
return <div>Dang!</div>
}自分の理解
Data APIs が使えるので、困ったら createBrowserRouter を使うのがよさそうだ。ただ、Data APIs は学習コストが高い。
エラーページではなく現在のページにエラーメッセージを出したいときは、Loader や Action の戻り値でエラーを返し、useLoaderData や useActionData で受け取ればよさそうだ。
関連
- React
- Remix
- Error Boundary
- React Hooks