メインコンテンツまでスキップ

Web Application 実装ガイド

React Router v7(Framework モード)を利用した推奨アプリケーション実装パターンを紹介します。

TODO List を表示・状態を更新する機能を実装

TODO List 一覧を表示し、状態を更新する処理を実装していきます。Todo 更新後に保存ボタンを押す動作も含まれますが、本カタログでは実際にDBなどに保存するといった動作は行わず、そのような処理が行われる想定でローダーを表示するところまでの動きを実装します。

  • Welcome page

    • /
  • Todo list page

    • /todolist

前提条件

root.tsx, layout など基本的なパーツの実装方法は一部省略されています。ただし、本カタログの内容は React Router v7 プロジェクト新規生成時のものとほぼ変わりません。

大まかな処理の流れ

推奨構成として、以下の順番で実装していきます。プロジェクト全体の初期セットアップ後を想定しています。

対象ファイル実装内容のサマリー
src/types/todo.d.tsTodo の型を定義
src/states/IsLoading.tsデータ処理中の状態を定義
src/hooks/useTodoSaver.tsxTodoを保存する Custom Hook を定義
src/components/TodoListItem.tsxTodoListの項目を再利用可能な構成として実装
src/pages/todolist/index.pages.tsxTodoListページのUIを定義
src/routes/todolist.tsxTodoList ページの起点とサーバー側処理(loader, action, 画面のエントリーポイント)
src/routes.tsTodoListページのルーティングを定義

他にも、必要に応じて適宜ディレクトリやファイルを構成してください。

src/types/todo.d.ts Todo の型を定義

以下のように型定義をしておきます。各コンポーネントでの Props 定義や元データの型宣言で利用されます。

export type Todo = {
id: string
title: string
status: 'pending' | 'inProgress' | 'completed'
isStarred: boolean
// ...
}

また、src/types/index.ts に以下のように定義しておくと、Import 分がシンプルになるため src/ 以下全ディレクトリに作成しておくと良いです。

export * from './Todo.d'

src/states/IsLoading.ts データ処理中の状態を定義

以下のように IsLoading の状態宣言をしておきます。本カタログでは Jotai というライブラリを利用します。

import { atom } from 'jotai'

export const IsLoadingAtom = atom<boolean>(false)

src/hooks/useTodoSaver.tsx Todoを保存する Custom Hook を定義

Todo の変更内容を保存する処理を実装します。本カタログでは実際の更新処理は行わないですが、PUT /todolist に更新された Todo を送信し、ログ出力する動作を実装します。

import { useAtom } from 'jotai'
import * as Atoms from '~/states'

export const useTodoSaver = () => {
const [isLoading, setIsLoading] = useAtom(Atoms.IsLoadingAtom)

const saveTodo = async (todo: Todo) => {
setIsLoading(true)
const saveResult = fetch('/todolist', {
method: 'PUT',
headers: { 'content-type': 'application/json', body: JSON.stringify(todo) },
})
.then((res) => res.json())
.finally(() => setIsLoading(false))

console.log('## saveResult - ', saveResult)
}

return { isLoading, saveTodo }
}

src/components/TodoListItem.tsx TodoListの項目を再利用可能な構成として実装

Todo 一覧の項目を実装していきます。このような一覧の項目となるコンポーネントは、他の画面での再利用できるような形を想定して配置・作成します。

import type { Todo } from '~/types'

type Props = {
todo: Todo
}

export const TodoListItem: React.FC<Props> = ({ todo }) => {

// Some dynamic ui handler methods

return (
<div className="bg-white rounded-lg border border-gray-200 p-6 hover:shadow-md transition-shadow">
<div className="flex items-start justify-between">
<div className="flex-1 min-w-0">
<div className="flex items-center gap-3 mb-3">
<h3 className="text-lg font-medium text-gray-900 truncate">{todo.title}</h3>

{ /* 中略。カタログ AMI に全てのサンプルコードが含まれています。 */ }

</div>
</div>
</div>
</div>
)
}

src/pages/todolist/index.pages.tsx TodoListページのUIを定義

Todo 一覧を表示させる TodoList ページの UI を実装していきます。

import { TodoListItem } from '~/components'
import type { Todo } from '~/types'

type Props = {
items: Todo[]
}

export const TodoListPage: React.FC<Props> = (props) => {
const { items } = props

return (
<>
<main className="max-w-6xl mx-auto px-4 py-8">

{ /* 中略。カタログ AMI に全てのサンプルコードが含まれています。*/ }

<div className="space-y-4">
{items.map((todo) => (
<TodoListItem key={todo.id} todo={todo} />
))}
</div>
</main>
</>
)
}

src/routes/todolist.tsx TodoList の起点・サーバー側処理を実装

src/routes/ 以下には、該当画面を読み込む際のサーバー側の処理(loader, action)と、meta タグなど各種設定が可能です。サーバー側からデータを取得し、前項の Page コンポーネントに Props 等の手段でデータを渡すことが可能で、loader を別ルートで実装し、非同期で GET /api/todolist のような形で SPA と同じようなアプローチを取ることも可能です。

import type { MetaFunction } from 'react-router'
import type { Route } from './+types/todolist'

import type { Todo } from '~/types'
import { TODO_ITEMS } from '~/fixtures'
import { TodoListPage } from '~/pages'

export const meta: MetaFunction = () => {
// biome-ignore format: for better readability
return [
{ title: 'New React Router App - Todo List Page' },
{ name: 'description', content: 'Welcome to React Router!' }
]
}

export async function loader({/* params (path parameters) */}: Route.LoaderArgs) {
return { TODO_ITEMS }
}

const TodoListRoute = ({ loaderData }: { loaderData: { TODO_ITEMS: Todo[] } }) => {
const { TODO_ITEMS } = loaderData
return <TodoListPage items={TODO_ITEMS} />
}

export default TodoListRoute

詳細は、React Router v7 の公式ドキュメントをご確認ください。

src/routes.ts TodoListページのルーティングを定義

最後に、画面を開いた時にどの routes ファイルにルーティングさせるかの設定を行います。ここでは、src/routes/todolist.tsx に振り分けられるように設定しておきます。

import { layout, type RouteConfig, route } from '@react-router/dev/routes'

const PREFIX = './routes'

export default [
// biome-ignore format: for better readability
layout('./layouts/default.tsx', [

// Welcome page
route('/', `${PREFIX}/welcome.tsx`),

// Todo list page
route('/todolist', `${PREFIX}/todolist.tsx`),

]),
] satisfies RouteConfig

動作確認

ここまで進めてきたら、開発モードでアプリケーションを立ち上げて動作確認を行います。ローカル実行によるデバッグとテストに進んでください。