Next.js 14でTodoアプリを作る – 7.データ一覧表示
投稿日:2024年03月15日
更新日:2024年03月15日
一覧表の作成
取得した全件データを表示する一覧表を作成します。
まずはテーブルtodos
に登録したデータも直接入力しています。
以下のコードのように<main>
と変更します。
<main className="text-center mx-auto max-w-2xl space-y-8">
<h1 className="text-3xl">Todo App</h1>
<table className="w-full">
<thead className="border-b-2 border-black/60">
<tr>
<th className="w-1/4 border-r-2 border-black/60">タイトル</th>
<th className=" w-3/4">内容</th>
</tr>
</thead>
<tbody>
<tr className="border-b border-black">
<td className="w-1/4 border-r-2 border-black/60">タスク1</td>
<td className="w-3/4">これはタスク1です</td>
</tr>
<tr className="border-b border-black">
<td className="w-1/4 border-r-2 border-black/60">タスク2</td>
<td className="w-3/4">これはタスク2です</td>
</tr>
<tr className="border-b border-black">
<td className="w-1/4 border-r-2 border-black/60">タスク3</td>
<td className="w-3/4">これはタスク3です</td>
</tr>
</tbody>
</table>
</main>
データの取得
データベースからテーブルtodos
のデータを全件取得します。
Next.jsのドキュメントを参考にします。
まずはテーブルtodos
のデータを取得する関数getTodos
を作成します。
以下のコードをファイルpage.tsx
のHome
コンポーネントの前に追加します。
async function getTodos() {
const res = await fetch("https://todo-app-d1-base.pages.dev/api/todos");
if (!res.ok) {
throw new Error('Failed to fetch data')
}
return res.json()
}
次に関数getTodos
を呼び出してデータを全件取得します。
まずは全件取得できたかどうかコンソールで確認します。Home
コンポーネントのreturn
の前に以下のコードを追加します。
const todos = await getTodos();
console.log(todos);
コンソールに取得したデータが表示されました。
[
{ id: 1, title: 'タスク1', content: 'これはタスク1です' },
{ id: 2, title: 'タスク2', content: 'これはタスク2です' },
{ id: 3, title: 'タスク3', content: 'これはタスク3です' }
]
データが1つのオブジェクトで全データが配列に格納されています。
全件取得したデータを表示
全件取得したデータを表に反映させます。
変数todos
からmap
関数で配列から変数todo
に1つづつデータを取得します。<table>
の<tbody>
の中を削除して以下のコードを追加します。
{todos.map((todo) => (
<tr className="border-b border-black" key={todo.id}>
<td className="w-1/4 border-r-2 border-black/60">{todo.title}</td>
<td className="w-3/4">{todo.content}</td>
</tr>
))}
これで一覧表に取得したデータが反映されました。
ただ、エラーが表示されています。
このままだと気持ち悪いので解決します。
エラーを解決
エラー内容を確認します。
エラーを確認するとtodos
とtodo
でエラーが発生しています。todos
のエラー内容は「todosはunknown型です。」とあります。todo
のエラー内容は「パラメーターtodoの型は暗黙的にanyになります。」とあります。
以上から変数todos
に型が設定されていないのが問題のようです。
まずは型を定義します。
ディレクトりtypes
内にファイルTodo.ts
を作成します。
ファイルTodo.ts
に型Todo
を定義します。
以下のコードを追加します。
type Todo = {
id: number;
title: string;
content: string;
};
次に変数todos
に型を定義します。
具体的にはデータの取得元である関数getTodos
の返り値に型を指定します。
関数getTodos
の返り値res.json()
に型Todo
を指定します。
以下のように、コードを変更します。
型Todo
の後に[]
をつけているのは、複数のオブジェクトが配列で格納されているからです。
取得したデータをコンソールで確かめたことを思い出してください。
return res.json<Todo[]>();
これでエラーが消えました。
一覧表のコンポーネントを作成
<table>
の部分を切り取って一覧表のコンポーネントを作成します。
ルートディレクトリにcomponents
ディレクトリを作成します。components
ディレクトリ内にTodoTable.tsx
を作成します。rafce
と入力してスペニットでコードを生成します。
const TodosTable = () => {
return <div>TodosTable</div>;
};
export default TodosTable;
作成したコンポーネントの<div>
にpage.tsx
の<table>
の部分を貼り付けます。map
関数で使うtodos
を引数で受け取りたいのでTodosProps
を定義します。
const TodosTable = ({ todos }: TodosProps) => {
return (
<table className="w-full">
<thead className="border-b-2 border-black/60">
<tr>
<th className="w-1/4 border-r-2 border-black/60">タイトル</th>
<th className=" w-3/4">内容</th>
</tr>
</thead>
<tbody>
{todos.map((todo) => (
<tr className="border-b border-black" key={todo.id}>
<td className="w-1/4 border-r-2 border-black/60">{todo.title}</td>
<td className="w-3/4">{todo.content}</td>
</tr>
))}
</tbody>
</table>
);
};
export default TodosTable;
TodosProps
はtypes
ディレクトリのTodo.ts
に追記します。
type TodosProps = {
todos: Todo[];
};
page.tsx
に切り取った<table>
の代わりにTodosTable
コンポーネントを追加します。
<main className="text-center mx-auto max-w-2xl space-y-8">
<h1 className="text-3xl">Todo App</h1>
<TodosTable todos={todos} />
</main>
TodosTable
コンポーネントをインポートします。
import TodosTable from "@/components/TodosTable";
関数getTodosを別ファイルに移動
関数getTodos
を別ファイルに移動します。
ルートディレクトリにutils
ディレクトリを作成します。utils
ディレクトリ内にapi.ts
を作成します。
export async function getTodos() {
const res = await fetch("https://todo-app-d1-base.pages.dev/api/todos");
if (!res.ok) {
throw new Error("Failed to fetch data");
}
return res.json<Todo[]>();
}
async
の前にexport
を追加して、他のファイルから呼び出すようにします。
そしてpage.tsx
に関数getTodos
をインポートします。
import { getTodos } from "@/utils/api";