Next.js 14でTodoアプリを作る – 8.データ新規登録
投稿日:2024年03月16日
更新日:2024年03月16日
フォームの作成
登録するフォームを作成します。<form>
の中に<label>
と<input>
を順に2組作成します。
1つ目の<label>
には「タイトル」を入力します。
2つ目の<label>
には「内容」を入力します。
<form className="flex flex-col">
<label>タイトル</label>
<input type="text" className="border-2 rounded-lg p-1" />
<label>内容</label>
<input type="text" className="border-2 rounded-lg p-1" />
<button
type="submit"
className="bg-black text-white rounded-lg w-full mt-4 p-1"
>
登録
</button>
</form>
typescript:TodoInput.tsx
"use client";
import { RefObject } from "react";
const TodoInput = (props: {
labelText: string;
ref: RefObject<HTMLInputElement>;
}) => {
return (
<>
<label>{props.labelText}</label>
<input ref={props.ref} type="text" className="border-2 rounded-lg p-1" />
</>
);
};
export default TodoInput;
typescript:TodoForm.tsx
<form className="flex flex-col">
<TodoInput labelText="タイトル" ref={titleRef} />
<TodoInput labelText="内容" ref={contentRef} />
<button
type="submit"
className="bg-black text-white rounded-lg w-full mt-4 p-1"
>
登録
</button>
</form>
新規作成
APIに接続してデータを新規作成します。
ルートディレクトリにutils
ディレクトリを作成します。
その中にファイルapi.ts
を作成します。
そして関数createTodo
を作成します。
export async function createTodo(todo: Todo) {
const res = await fetch("https://todo-app-d1-base.pages.dev/api/todos", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(todo),
});
if (!res.ok) {
throw new Error("Failed to fetch data");
}
return res.json<Todo>();
}
フォームで入力されたデータを取得
"use client";
import { createTodo } from "@/utils/api";
import { useRouter } from "next/navigation";
import { useRef } from "react";
const TodoForm = () => {
const titleRef = useRef<HTMLInputElement>(null);
const contentRef = useRef<HTMLInputElement>(null);
const router = useRouter();
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const title = titleRef.current?.value;
const content = contentRef.current?.value;
if (!title || !content)
return console.log("タイトルと内容を入力してください");
console.log(`タイトル:${title} 内容:${content}`);
createTodo({ title, content });
router.refresh();
};
return (
<form className="flex flex-col" onSubmit={handleSubmit}>
<label>タイトル</label>
<input ref={titleRef} type="text" className="border-2 rounded-lg p-1" />
<label>内容</label>
<input ref={contentRef} type="text" className="border-2 rounded-lg p-1" />
<button
type="submit"
className="bg-black text-white rounded-lg w-full mt-4 p-1"
>
登録
</button>
</form>
);
};
export default TodoForm;
作成したTodoForm
コンポーネントをファイルpage.tsx
に追加します。
<main className="text-center mx-auto max-w-2xl space-y-8">
<h1 className="text-3xl">Todo App</h1>
<TodoForm />
<TodosTable todos={todos} />
</main>
TodoForm
コンポーネントをインポートします。
import TodosTable from "@/components/TodosTable";
ランタイムエッジを追加
プロジェクトをデプロイします。
以下のコマンドを実行します。
npm run pages:deploy
ところが次のようなエラーが発生します。
⚡️ ERROR: Failed to produce a Cloudflare Pages build from the project.
⚡️
⚡️ The following routes were not configured to run with the Edge Runtime:
⚡️ - /index
⚡️
⚡️ Please make sure that all your non-static routes export the following edge runtime route segment config:
⚡️ export const runtime = 'edge';
⚡️
⚡️ You can read more about the Edge Runtime on the Next.js documentation:
⚡️ https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes
エラー内容を見ると、/index
にexport const runtime = 'edge';
が必要なようです。/index
とはトップページだと認識して、page.tsx
にexport const runtime = 'edge';
を追加します。
export const runtime = "edge";
プロジェクトを再びデプロイします。
無事にデプロイされました。
新規登録後に一覧画面を更新
デプロイしたアプリの動作を確認します。https://todo-app-d1-base.pages.dev/
にアクセスします。
表示されたら、データを新規登録してみます。
ところが、新規登録した後に一覧画面が更新されません。
これはブラウザにキャッシュされていて、データが更新されないためです。
そこで関数createTodo
のcache
オプションにno-store
を追加します。
export async function createTodo(todo: Todo) {
const res = await fetch("https://todo-app-d1-base.pages.dev/api/todos", {
cache: "no-store",
});
if (!res.ok) {
throw new Error("Failed to fetch data");
}
return res.json<Todo>();
}
再びデプロイします。
アプリで再び新規登録してみます。
今度は無事に一覧表示が更新されました。