forked from semi-23e/nextjs-todo-tutorial
pahse4-3まで データを永続化
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react'; // useEffectを追加
|
||||||
|
|
||||||
// Todo型の定義
|
// Todo型の定義
|
||||||
type Todo = {
|
type Todo = {
|
||||||
@@ -12,6 +12,9 @@ type Todo = {
|
|||||||
type FilterType = 'all' | 'active' | 'completed'; // フィルターの種類。全て、未完了、完了の3種類。
|
type FilterType = 'all' | 'active' | 'completed'; // フィルターの種類。全て、未完了、完了の3種類。
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
// Local Storage のキー
|
||||||
|
const STORAGE_KEY = 'todo-app-data';
|
||||||
|
|
||||||
// Todoリスト本体の状態管理
|
// Todoリスト本体の状態管理
|
||||||
const [todos, setTodos] = useState<Todo[]>([]); // Todo配列を保持
|
const [todos, setTodos] = useState<Todo[]>([]); // Todo配列を保持
|
||||||
|
|
||||||
@@ -141,11 +144,78 @@ export default function Home() {
|
|||||||
setShowInputAlert(false); // 警告ダイアログを閉じる
|
setShowInputAlert(false); // 警告ダイアログを閉じる
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// アプリ起動時にデータを読み込み
|
||||||
|
useEffect(() => {
|
||||||
|
const savedTodos = localStorage.getItem(STORAGE_KEY);
|
||||||
|
if (savedTodos) {
|
||||||
|
try {
|
||||||
|
const parsedTodos = JSON.parse(savedTodos);
|
||||||
|
setTodos(parsedTodos);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('データの読み込みに失敗しました:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, []); // 空の依存配列で初回のみ実行
|
||||||
|
|
||||||
|
// TODOが変更されるたびに保存
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
|
||||||
|
}, [todos]); // todosが変更されるたびに実行
|
||||||
|
|
||||||
|
// データのエクスポート機能
|
||||||
|
const exportData = () => {
|
||||||
|
const dataStr = JSON.stringify(todos, null, 2);
|
||||||
|
const dataBlob = new Blob([dataStr], { type: 'application/json' });
|
||||||
|
const url = URL.createObjectURL(dataBlob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.download = 'todos.json';
|
||||||
|
link.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
// データのインポート機能
|
||||||
|
const importData = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const file = event.target.files?.[0];
|
||||||
|
if (file) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
try {
|
||||||
|
const importedTodos = JSON.parse(e.target?.result as string);
|
||||||
|
setTodos(importedTodos);
|
||||||
|
} catch (error) {
|
||||||
|
alert('ファイルの読み込みに失敗しました');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// JSX: 実際に表示されるUI定義
|
// JSX: 実際に表示されるUI定義
|
||||||
return (
|
return (
|
||||||
<main className="min-h-screen p-8 bg-gray-50"> {/* 全体の背景や余白 */}
|
<main className="min-h-screen p-8 bg-gray-50"> {/* 全体の背景や余白 */}
|
||||||
<div className="max-w-md mx-auto bg-white rounded-lg shadow-md p-6"> {/* カード風の白背景 */}
|
<div className="max-w-md mx-auto bg-white rounded-lg shadow-md p-6"> {/* カード風の白背景 */}
|
||||||
<h1 className="text-2xl font-bold text-gray-800 mb-6">TODOアプリ</h1> {/* タイトル */}
|
{/* ヘッダー部分にエクスポート・インポート機能を追加 */}
|
||||||
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
<h1 className="text-2xl font-bold text-gray-800">TODOアプリ</h1>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button
|
||||||
|
onClick={exportData}
|
||||||
|
className="text-blue-500 hover:text-blue-700 px-3 py-1 rounded transition-colors text-sm"
|
||||||
|
>
|
||||||
|
エクスポート
|
||||||
|
</button>
|
||||||
|
<label className="text-blue-500 hover:text-blue-700 px-3 py-1 rounded transition-colors cursor-pointer text-sm">
|
||||||
|
インポート
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
accept=".json"
|
||||||
|
onChange={importData}
|
||||||
|
className="hidden"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 入力フォーム */}
|
{/* 入力フォーム */}
|
||||||
<form onSubmit={handleSubmit} className="mb-4"> {/* フォーム全体のスタイル */}
|
<form onSubmit={handleSubmit} className="mb-4"> {/* フォーム全体のスタイル */}
|
||||||
|
|||||||
Reference in New Issue
Block a user