サイト名変更・お引越しのお知らせ

【React Router v6系】React Routerのloaderの基本を解説

React Router バージョン6系でloaderの基本を解説します。

例としてTodo一覧と詳細を表示するようなアプリを作ります。

モグモグさん

loaderはv6.4から登場した新しい機能です。

バージョン
  • react 18.2.0
  • react-router-dom 6.6.1

モグモグさん

本記事ではTypeScriptを使っています。

使っていない方は、tsxjsx、型定義を削除するなど適時置き換えていただけますと幸いです。

ルーティングの基本についてはこちらで解説しています。

【React Router v6系】React Routerを導入し基本的なページ遷移を行う方法を解説

Todo一覧を取得して表示する

まずはルーティングとloaderを定義していきます。

例なので出来るだけシンプルな形で定義していきます。

ルーティング定義とloaderに関数を渡す

import React from "react";
import { createBrowserRouter } from "react-router-dom";
import { ErrorPage } from "./screens/Error";
import { Layout } from "./screens/Layout";
import { TodoList } from "./screens/TodoList";

// Todoの型
export type Todo = {
  id: number;
  title: string;
  completed: boolean;
};

// サンプルデータ(実際はAPIから取得するケースが多い)
const sampleTodoList: Todo[] = [
  { id: 1, title: "Todo 1", completed: false },
  { id: 2, title: "Todo 2", completed: false },
  { id: 3, title: "Todo 3", completed: true },
];

const fetchTodoList = async () => {
  return sampleTodoList;
};

// サンプルデータを返す関数
const loader = async () => {
  return await fetchTodoList();
};

export const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />,
    errorElement: <ErrorPage />,
    children: [{ path: "todo-list", element: <TodoList />, loader: loader }],
  },
]);

loaderには関数を渡すことができます。

loaderはページが表示される前に実行されるため、今までuseEffect内などで行っていたことが事前にできるようになります。

それによってユーザー体験の向上が見込めます。

もちろんクライアントサイドで行っていますが、サーバーサイドレンダリングに近いと思います。

loader内では、データを取得しデータを返却します。

ルートファイルでルーティングを定義したファイルを呼び出す

ルーティングを定義したAppRoutesをルートから呼び出します

モグモグさん

ルートファイルはみなさんの環境に合わせてください!

本記事では、create-react-appを使っているのでsrc/index.tsxがルートファイルになります。

ルーティングを定義したファイルを作成せず、ルートファイルに直接書いてももちろんOKです。

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import reportWebVitals from "./reportWebVitals";
import { RouterProvider } from "react-router-dom";
import { router } from "./components/AppRoutes";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

loaderで返したデータをページ内で取得する

loader内で返したTodo一覧をTodoListページで取得し表示します。

import { FC } from "react";
import { useLoaderData } from "react-router-dom";
import { Todo } from "../AppRoutes";

export const TodoList: FC = () => {
  const todoList = useLoaderData() as Todo[];
  console.log("data", todoList);

  return (
    <div>
      <h1>Todo List</h1>
      <ul>
        {todoList.map((todo) => (
          <li key={todo.id}>{todo.title}</li>
        ))}
      </ul>
    </div>
  );
};

useLoaderDataを使うことで、loaderで返したデータが取得できます。

todoリスト

/todo-listにアクセスするとこんな感じでTodo一覧が表示されました!

エラーハンドリング

続いてエラーハンドリングをしていきましょう。

APIを叩いているわけではないので、擬似的にエラーを発生させています。

import React from "react";
import { createBrowserRouter } from "react-router-dom";
import { ErrorPage } from "./screens/Error";
import { Layout } from "./screens/Layout";
import { TodoList } from "./screens/TodoList";

export type Todo = {
  id: number;
  title: string;
  completed: boolean;
};

const sampleTodoList: Todo[] = [
  { id: 1, title: "Todo 1", completed: false },
  { id: 2, title: "Todo 2", completed: false },
  { id: 3, title: "Todo 3", completed: true },
];

export const fetchTodoList = async () => {
  return sampleTodoList;
};

const loader = async () => {
  // 500エラーを返す
  throw new Response("500 Error!", { status: 500 });
  return await fetchTodoList();
};

export const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />,
    errorElement: <ErrorPage />,
    children: [{ path: "todo-list", element: <TodoList />, loader: loader }],
  },
]);

loaderでエラーが発生すると、errorElementで渡しているページでエラーを取得できます。

エラーの内容はuseRouteErrorで取得できます。

import { FC } from "react";
import { Link, useRouteError } from "react-router-dom";

export const ErrorPage: FC = () => {
  const error = useRouteError();
  console.log(error);

  return (
    <div>
      <h1>Error!</h1>
      <p>
        <Link to="/">Go back to home.</Link>
      </p>
    </div>
  );
};

エラーログ

500エラーのログが表示されました。

例えばステータスでエラーの内容を変えたりすることができますね。

Todo詳細を取得して表示する

Dynamic Routingの基本についてはこちらで解説しています。

【React Router v6系】React RouterでDynamic Routingを実装する方法を解説

詳細ページの定義とloader作成

import React from "react";
import { createBrowserRouter, LoaderFunctionArgs } from "react-router-dom";
import { ErrorPage } from "./screens/Error";
import { Layout } from "./screens/Layout";
import { TodoList } from "./screens/TodoList";

export type Todo = {
  id: number;
  title: string;
  completed: boolean;
};

const sampleTodoList: Todo[] = [
  { id: 1, title: "Todo 1", completed: false },
  { id: 2, title: "Todo 2", completed: false },
  { id: 3, title: "Todo 3", completed: true },
];

export const fetchTodoList = async () => {
  return sampleTodoList;
};

const todoListLoader = async () => {
  return await fetchTodoList();
};

const todoDetailLoader = async ({ params }: LoaderFunctionArgs) => {
  const todo = sampleTodoList.find((todo) => todo.id === Number(params.id));
  if (!todo) {
    throw new Response("Todo not found", { status: 404 });
  }
  return todo;
};

export const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />,
    errorElement: <ErrorPage />,
    children: [
      { path: "todo-list", element: <TodoList />, loader: todoListLoader },
      { path: ":id", element: <TodoDetail />, loader: todoDetailLoader }, // TodoDetailはこの後作成
    ],
  },
]);

詳細ページ作成

import { FC } from "react";
import { useLoaderData } from "react-router-dom";
import { Todo } from "../AppRoutes";

export const TodoDetail: FC = () => {
  const todo = useLoaderData() as Todo;

  return (
    <div>
      <h1>{todo.title}</h1>
      <p>status: {todo.completed ? "完了" : "未完了"}</p>
    </div>
  );
};

動作確認

todo詳細画面

/1にアクセスするとこんな感じで詳細ページが表示されます。

/100などの存在しないページにアクセスするとエラーページがレンダリングされます。

まとめ

React Router バージョン6系でloaderの基本を解説しました。

loaderを使ってより良いユーザー体験を目指してみてください。