From cd0e6cca9f456dbd3f3505e061774fe41850e710 Mon Sep 17 00:00:00 2001 From: KentaroKumode <23e1273@andrew.ac.jp> Date: Sat, 12 Jul 2025 17:25:15 +0900 Subject: [PATCH] =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E5=85=A8=E4=BD=93?= =?UTF-8?q?=E3=81=AB=E8=B3=AA=E7=96=91=E5=BF=9C=E7=AD=94=E8=A3=9C=E5=8A=A9?= =?UTF-8?q?=E7=94=A8=E3=81=AE=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88=E3=82=A2?= =?UTF-8?q?=E3=82=A6=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/page.tsx | 123 +++++++++++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 48 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index fc216f5..78da9b6 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,96 +1,112 @@ -'use client'; // ← 重要!状態管理を使う時は必須 +'use client'; // ← これはNext.jsのApp Routerで、クライアントコンポーネントとして実行させる指示。useStateなどクライアント専用フックを使うために必須。 -import { useState } from 'react'; +import { useState } from 'react'; // Reactの"状態管理用"のフック。状態が変化したら再レンダリングが起きる。 -// TypeScriptの型定義 +// Todo型の定義。オブジェクトが必ずid, text, completedというプロパティを持つことを保証。 type Todo = { - id: number; // 一意の識別子 - text: string; // TODOの内容 - completed: boolean; // 完了状態 + id: number; // ユニークな識別子。今回はDate.now()で一意性をある程度確保。 + text: string; // TODOの内容。ユーザーが入力する文字列。 + completed: boolean; // 完了したかどうかのフラグ。チェックボックスに対応。 }; -export default function Home() { - // useState フックの使い方 - const [todos, setTodos] = useState([]); - const [inputValue, setInputValue] = useState(''); +export default function Home() { // Reactコンポーネント。Next.jsのデフォルトエクスポートとしてトップページに描画される。 + // 状態変数定義。todosはTodoの配列、inputValueは入力中のテキスト。 + const [todos, setTodos] = useState([]); // Todoリスト本体。初期値は空配列。 + const [inputValue, setInputValue] = useState(''); // 入力欄の中身。初期値は空文字。 + const [showConfirm, setShowConfirm] = useState(false); // 削除確認ダイアログを表示するかどうか。 + const [targetId, setTargetId] = useState(null); // 確認ダイアログで削除対象のTodoのIDを一時保持。 + + // 新しいTODOを追加する関数 const addTodo = () => { - // 空の入力をチェック(バリデーション) - if (inputValue.trim() === '') { + if (inputValue.trim() === '') { // 入力が空欄だったら処理を中止。trim()で空白も除去。 alert('TODOを入力してください'); return; } - const newTodo: Todo = { - id: Date.now(), // 簡易的なID生成(本番ではuuidを推奨) - text: inputValue, - completed: false + const newTodo: Todo = { + id: Date.now(), // 現在時刻をミリ秒単位でIDとして使用。実運用ではuuid推奨。 + text: inputValue, // 入力欄の内容を設定。 + completed: false // 初期状態では未完了。 }; - setTodos([...todos, newTodo]); - setInputValue(''); - }; - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); // ページのリロードを防ぐ - addTodo(); + setTodos([...todos, newTodo]); // 既存の配列に新しいTodoを追加。イミュータブルに保つため新配列を生成。 + setInputValue(''); // 入力欄を初期化。 }; + // フォームが送信されたときの処理 + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); // ページ全体のリロードを防ぐ。SPA的挙動にする。 + addTodo(); // addTodo関数を実行。 + }; + + // Todoの完了状態をトグル(反転)する関数 const toggleTodo = (id: number) => { - // mapを使って新しい配列を作成(イミュータビリティ) const updatedTodos = todos.map(todo => { if (todo.id === id) { - // 該当するTODOを見つけたら、completedを反転 - return { ...todo, completed: !todo.completed }; + return { ...todo, completed: !todo.completed }; // 対象のcompletedだけ反転。 } - return todo; // それ以外はそのまま + return todo; // それ以外はそのまま。 }); - - setTodos(updatedTodos); + setTodos(updatedTodos); // 新しい配列をset。Reactはこれを検知して再描画する。 }; - const deleteTodo = (id: number) => { - // 確認ダイアログを表示(オプション) - if (confirm('本当に削除しますか?')) { - // filterを使って該当ID以外の要素で新しい配列を作成 - const filteredTodos = todos.filter(todo => todo.id !== id); - setTodos(filteredTodos); + // 削除ボタンが押されたときの仮処理。確認ダイアログを表示。 + const handleDeleteClick = (id: number) => { + setShowConfirm(true); // ダイアログ表示 + setTargetId(id); // 対象ID記憶 + }; + + // ダイアログで「はい」が押されたときの本削除処理 + const confirmDelete = () => { + if (targetId !== null) { // nullチェックを忘れずに + setTodos(todos.filter(todo => todo.id !== targetId)); // 該当ID以外を残す + setShowConfirm(false); // ダイアログを閉じる + setTargetId(null); // 状態を初期化 } }; + // ダイアログで「いいえ」が押されたときの処理 + const cancelDelete = () => { + setShowConfirm(false); + setTargetId(null); + }; + + // JSX: 実際に表示されるUI定義 return ( -
-
+
{/* 全体の背景や余白 */} +
{/* カード風の白背景 */}

TODOアプリ

-{/* JSX部分 */} + + {/* 入力フォーム */}
setInputValue(e.target.value)} + value={inputValue} // stateと連動 + onChange={(e) => setInputValue(e.target.value)} // 入力をリアルタイムに反映 placeholder="TODOを入力してEnterキー" className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" /> -
+ + {/* TODOリスト */}
{todos.map((todo) => (
toggleTodo(todo.id)} + checked={todo.completed} // 完了状態を反映 + onChange={() => toggleTodo(todo.id)} // トグル動作 className="mr-3 h-5 w-5 cursor-pointer" /> - + {todo.text}
))}
+ + {/* 確認ダイアログ */} + {showConfirm && ( +
+
+

本当に削除しますか?

+ + +
+
+ )}
); -} \ No newline at end of file +}