🖥️
Next.js入門8 Todoアプリ(タスク編集, 削除)
記事一覧
- Next.js × Docker 最速環境構築
- Next.js入門1 ページ追加
- Next.js入門2 コンポーネント
- Next.js入門3 無記名関数 & イベントハンドラー
- Next.js入門4 Hooks
- Next.js入門5 Todoアプリ(タスク一覧)
- Next.js入門6 Todoアプリ(タスク追加)
- Next.js入門7 Todoアプリ(タスク詳細)
- Next.js入門8 Todoアプリ(タスク編集, 削除)
タスク編集, 削除
ページを編集
詳細ページから編集, 削除をできるようにする
-> 編集はmap
関数を使う
-> 削除はfilter
関数を使う
-
task/[index]/page.tsx
を編集
"use client";
import { use, useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { Button } from "@/components/button";
import { Task } from "@/types/task";
// Modalコンポーネントをインポート
import { Modal } from "@/components/modal";
type Props = {
params: Promise<{
index: string;
}>;
};
function Page({ params }: Props) {
const index = parseInt(use(params).index);
const router = useRouter();
const [task, setTask] = useState<Task>();
const [tasks, setTasks] = useState<Task[]>([]);
// モーダルの状態を管理する
const [isOpen, setIsOpen] = useState(false);
useEffect(() => {
const storedTasks = localStorage.getItem("tasks");
if (storedTasks) {
const parsedTasks = JSON.parse(storedTasks);
setTasks(parsedTasks);
setTask(parsedTasks[index]);
}
}, [index]);
if (!task) {
return;
}
return (
<div className="flex flex-col gap-8 items-center p-16">
<h1 className="text-4xl font-bold">タスク詳細</h1>
<div className="w-full flex flex-col gap-8 border rounded-2xl p-8">
<div className="flex gap-2 border-b">
<label className="font-bold">タスク名</label>
<p>{task.title}</p>
</div>
<div className="flex flex-col gap-2">
<label className="font-bold border-b">内容</label>
<p>{task.content}</p>
</div>
</div>
<div className="w-full flex justify-between">
<Button
onClick={() => {
router.push("/task");
}}
>
戻る
</Button>
{/* 編集ボタンと削除ボタン */}
<div className="flex gap-2">
<Button onClick={() => setIsOpen(true)}>編集</Button>
{/* 【重要】削除処理 */}
<Button
onClick={() => {
// filterでindex番目以外のタスクを取得
const newTasks = tasks.filter((_, i) => i !== index);
// localStorageを更新
localStorage.setItem("tasks", JSON.stringify(newTasks));
// タスク一覧ページに遷移
router.push("/task");
}}
>
削除
</Button>
</div>
</div>
{/* isOpenがtrueの場合のみModalを表示 */}
{isOpen && (
// ModalコンポーネントのonCloseにモーダルを閉じる処理を渡す
<Modal onClose={() => setIsOpen(false)}>
<form className="flex flex-col gap-4 text-black border-black">
{/* タスク名入力欄 */}
<div className="flex flex-col gap-2">
<label>タスク名</label>
<input
className="border p-2 rounded-xl"
value={task.title}
onChange={(e) => setTask({ ...task, title: e.target.value })}
/>
</div>
{/* タスク内容入力欄 */}
<div className="flex flex-col gap-2">
<label>内容</label>
<textarea
className="border p-2 rounded-xl"
value={task.content}
onChange={(e) => setTask({ ...task, content: e.target.value })}
/>
</div>
{/* キャンセルボタンと更新ボタン */}
<div className="flex justify-end gap-4">
<Button onClick={() => setIsOpen(false)}>キャンセル</Button>
<Button
// 【重要】更新処理
onClick={() => {
// mapでindex番目のタスクのみ更新
const newTasks = tasks.map((t, i) =>
i === index ? task : t
);
// localStorageを更新
localStorage.setItem("tasks", JSON.stringify(newTasks));
// タスク一覧ページに遷移
router.push("/task");
}}
>
更新
</Button>
</div>
</form>
</Modal>
)}
</div>
);
}
export default Page;
Discussion