React Router V6でOutletを利用したページコンテンツの遅延読み込み
リリースされたばかりのReact Router V6のOutletを使って、ページコンポーネントを遅延読み込みするいい感じの方法を考えた。
ページコンポーネントを雑に遅延読み込みさせていると、各ページの初回読み込み時にチラツキが発生してしまう場合がある。
またSuspenseを利用してLoading状態を作るにしても、レイアウトを構成するヘッダーなども一緒に含まれてしまうと、ユーザーはアプリが壊れたような印象を受けてしまう。
そこで、React Router v6のOutletを利用して、ページのコンテンツのみが遅延読み込みされるように、ページのコンテンツとレイアウトを分離して次のようなコードを書いた。
const listPagePreloading = import("../pages/list");
const ListPage = React.lazy(() => listPagePreloading);
const itemPagePreloading = import("../pages/items");
const ItemPage = React.lazy(() => itemPagePreloading);
const editItemPagePreloading = import("../pages/items/edit");
const EditItemPage = React.lazy(() => editItemPagePreloading);
const protectedRoutes: RouteObject[] = [
{
path: "/",
element: <RequireAuth><Outlet /></RequireAuth>,
children: [
{
index: true,
element: <Navigate to="/list" replace />
},
{
path: "/list",
element: <ListPageLayout />
children: [
{
index: true,
element: <ListPage />
}
]
},
{
path: "/items/:itemId",
element: <ItemPageLayout />,
children: [
{
index: true,
element: <ItemPage />
},
{
path: "/edit",
element: <EditItemPage />
}
]
}
]
}
]
ディレクトリ構造は次のようにして、レイアウトとページコンテンツはファイルを分離している。
- pages/
- list/
- layout.tsx
- index.tsx
- items/
- index.tsx
- layout.tsx
- edit/
- index.tsx
...
RequireAuthの実装は次のようになっている。
const RequireAuth = ({ children }: { children: JSX.Element }) => {
const auth = useAuth();
const location = useLocation();
if (!auth.authenticated) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
};