🖥️
Next.js入門6 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アプリ(タスク編集, 削除)
ローカルストレージ
ローカルストレージとは
Webブラウザにデータを永続的に保存できる仕組み
追加
- 値の保存
// nameという名前で"ケイト"をローカルストレージに保存
localStorage.setItem("name", "ケイト");
- オブジェクトの保存
const user = { name: "ケイト", age: 18 };
// JSON.stringifyでjsonにして保存する(取得の際にオブジェクトに戻す)
localStorage.setItem("user", JSON.stringify(user));
削除
// userという名前のローカルストレージを削除
localStorage.removeItem("user");
取得
- 値の取得
// userという名前のローカルストレージを取得
// 存在しない場合はnullになる
const user = localStorage.getItem("user");
- オブジェクトの取得
const userJson = localStorage.getItem("user");
let user = null;
if (userJson) {
// JSON.parseでjsonからオブジェクトに変換
user = JSON.parse(userJson);
}
タスク追加
ローカルストレージを用いてタスクの追加、保存ができるようにする
コンポーネント作成
-
button.tsx
とmodal.tsx
を作成
.
├── next-app
│ └── components
│ ├── button.tsx # ここに作成
│ └── modal.tsx # ここに作成
├── docker-compose.yml
└── Dockerfile
-
button.tsx
を記述
// 引数の型を定義
type Props = {
// ボタンが押された時に実行する関数
onClick: () => void;
// ボタンの中身
children: React.ReactNode;
};
export function Button({ onClick, children }: Props) {
return (
<button
type="button"
className="w-fit border rounded-xl p-3 font-bold"
// クリック時に渡されたonClick関数を実行する
onClick={() => onClick()}
>
{children}
</button>
);
}
-
modal.tsx
を記述
// 引数の型を定義
type Props = {
// モーダルを閉じる関数
onClose: () => void;
// モーダルの中身
children: React.ReactNode;
};
export function Modal({ onClose, children }: Props) {
return (
<div className="fixed top-0 left-0 w-full h-full flex justify-center items-center">
<div
className="absolute top-0 left-0 w-full h-full bg-black/[0.5]"
// 背景をクリック時に渡されたonCloseを実行
onClick={onClose}
/>
<div className="relative bg-white rounded-lg p-8 w-1/2 z-10">
<button
className="absolute top-5 right-5 text-gray-500 hover:text-black"
// 閉じるボタンをクリック時に渡されたonCloseを実行
onClick={onClose}
>
閉じる
</button>
{children}
</div>
</div>
);
}
ページ編集
-
task/page.tsx
を編集
"use client";
import { useEffect, useState } from "react";
// Buttonコンポーネントをインポート
import { Button } from "@/components/button";
// Modalコンポーネントをインポート
import { Modal } from "@/components/modal";
import { Table } from "@/components/table";
import { Task } from "@/types/task";
function Page() {
// タスクの初期値を空にする
const [tasks, setTasks] = useState<Task[]>([]);
// モーダルの状態を管理する
const [isOpen, setIsOpen] = useState(false);
// 新しいタスクを管理する
const [newTask, setNewTask] = useState<Task>({
title: "",
content: "",
});
// ローカルストレージからタスクを取得する処理
useEffect(() => {
// ローカルストレージからタスクを取得
const tasks = localStorage.getItem("tasks");
// タスクがある場合はタスクを設定
if (tasks) {
setTasks(JSON.parse(tasks));
}
}, []);
// タスクをローカルストレージに保存する処理
useEffect(() => {
if (tasks.length > 0) {
// タスクがある場合はローカルストレージに保存
localStorage.setItem("tasks", JSON.stringify(tasks));
} else {
// タスクがない場合はローカルストレージから削除
localStorage.removeItem("tasks");
}
}, [tasks]); // tasksが変更されたら実行
return (
<div className="flex flex-col gap-8 items-center p-16">
<h1 className="text-4xl font-bold">タスク一覧</h1>
{/* モーダルを表示するボタン */}
<div className="w-full flex justify-end">
<Button onClick={() => setIsOpen(true)}>タスクを追加</Button>
</div>
<Table tasks={tasks}></Table>
{/* 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={newTask.title}
onChange={(e) =>
setNewTask({ ...newTask, title: e.target.value })
}
/>
</div>
{/* タスク内容入力欄 */}
<div className="flex flex-col gap-2">
<label>内容</label>
<textarea
className="border p-2 rounded-xl"
value={newTask.content}
onChange={(e) =>
setNewTask({ ...newTask, content: e.target.value })
}
/>
</div>
{/* キャンセルボタンと追加ボタン */}
<div className="flex justify-end gap-4">
<Button onClick={() => setIsOpen(false)}>キャンセル</Button>
<Button
// 【重要】追加処理
onClick={() => {
setTasks([...tasks, newTask]); // タスクの追加
setIsOpen(false); // モーダルを閉じる
setNewTask({ title: "", content: "" }); // 新しいタスクを初期化
}}
>
追加
</Button>
</div>
</form>
</Modal>
)}
</div>
);
}
export default Page;
http://localhost:3000/taskからタスクの追加ができればOK
リロードしても消えないことが確認できるとGood
Discussion