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";