Reactで3ページ構成のlacalStorageを使用したTodoアプリを作ってみて
細かい機能をまとめようと書いてみました
lacalStorageとは
ローカルストレージは、キーと値の形式でデータを保存するブラウザAPIの一部で、ページを再読み込みしてもデータが保持されるのが特徴です。
基本ステップ
//値を保存する localStorage.setItem(key, value)を使用して、値をローカルストレージに保存します。
localStorage.setItem("username", "Takasuki");
// 値を取得する localStorage.getItem(key)で、保存された値を取得します。
const username = localStorage.getItem("username");
console.log(username); // "Tokumei"
//値を削除する localStorage.removeItem(key)で特定のキーを削除します。
localStorage.removeItem("username");
// すべてのデータをクリアする 全てのデータを削除するにはlocalStorage.clear()を使います。
localStorage.clear();
実際の使用
Todoリストに追加の実装
まず定数を設定してローカルストレージの準備
const navigate = useNavigate();
const handleAddTask = (newTask) => {
const storedTasks = JSON.parse(localStorage.getItem('tasks')) || [];
const updatedTasks = [...storedTasks, { id: storedTasks.length + 1, ...newTask }];
localStorage.setItem('tasks', JSON.stringify(updatedTasks));
navigate('/');
};
作成した関数を設定
ついでに内容をコンポーネントにTaskFormを作成して引っ張ってくる
import TaskForm from '../components/TaskForm';
return (
<div className="w-full max-w-lg bg-white shadow-md rounded-lg p-6">
<TaskForm onAddTask={handleAddTask} />
</div>
);
components/TaskFormに以下を記述してもってこれるように
import React, { useState } from 'react';
const TaskForm = ({ onAddTask }) => {
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (!title.trim() || !description.trim()) return;
onAddTask({ title, description });
setTitle('');
setDescription('');
};
return (
<form onSubmit={handleSubmit}>
<div className='flex justify-between'>
<label >タイトル</label>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
required
className='border w-2/3'
/>
</div>
<div className='flex justify-between'>
<label>詳細</label>
<input
type="text"
value={description}
onChange={(e) => setDescription(e.target.value)}
required
className=' border w-2/3'
/>
</div>
<button type="submit" className=' block mx-auto' >タスクの追加</button>
</form>
);
};
export default TaskForm;
これで追加ができるはずです
一覧の表示
Home画面より一覧表示を設定、詳細をcomponents/TaskListより記述して引っ張ってきてます
reactのルートについては別途記入します
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import TaskList from "../components/TaskList";
const [tasks, setTasks] = useState([]);
useEffect(() => {
const storedTasks = JSON.parse(localStorage.getItem("tasks")) || [];
setTasks(storedTasks);
}, []);
return (
<div className="min-h-screen bg-gray-100 flex flex-col items-center p-6">
<h1 className="text-3xl font-bold text-gray-800 mb-6">To-Do リスト</h1>
<div className="w-full max-w-2xl bg-white shadow-md rounded-lg p-4">
<TaskList tasks={tasks} />
</div>
<Link
to="/add"
className="mt-6 px-6 py-3 bg-blue-500 text-white font-semibold rounded-lg shadow hover:bg-blue-600 transition"
>
タスクを追加
</Link>
</div>
);
};
export default Home;
マップ構文より順々にtasksからidとdescriptonを引っ張ってこれるようにしてます
import React from 'react';
import { Link } from 'react-router-dom';
const TaskList = ({ tasks }) => {
return (
<div>
<h2>Task List</h2>
{tasks.map((task) => (
<div key={task.id}>
<Link to={`/task/${task.id}`}>
<h3>{task.title}</h3>
</Link>
<p>{task.description}</p>
</div>
))}
</div>
);
};
export default TaskList;
削除機能
詳細ページで削除機能を入れてみました
import React, { useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import TaskDetail from '../components/TaskDetail';
const TaskDetailPage = () => {
const { id } = useParams();
const navigate = useNavigate();
const [task, setTask] = useState(null);
useEffect(() => {
const storedTasks = JSON.parse(localStorage.getItem('tasks')) || [];
const foundTask = storedTasks.find((t) => t.id === Number(id));
setTask(foundTask);
}, [id]);
// タスク削除関数
const handleDeleteTask = () => {
const storedTasks = JSON.parse(localStorage.getItem('tasks')) || [];
const updatedTasks = storedTasks.filter((t) => t.id !== Number(id));
localStorage.setItem('tasks', JSON.stringify(updatedTasks));
navigate('/'); // 削除後にタスク一覧へリダイレクト
};
return (
<div className="min-h-screen bg-gray-100 flex flex-col items-center p-6">
<h2 className="text-2xl font-bold text-gray-800 mb-6">タスクの詳細</h2>
<div className="w-full max-w-lg bg-white shadow-md rounded-lg p-6">
{task ? (
<>
<TaskDetail task={task} />
<button
onClick={handleDeleteTask}
className="mt-6 px-6 py-3 bg-red-500 text-white font-semibold rounded-lg shadow hover:bg-red-600 transition"
>
削除
</button>
</>
) : (
<p className="text-gray-500">タスクが見つかりません</p>
)}
</div>
<button
onClick={() => navigate('/')}
className="mt-6 px-6 py-3 bg-gray-500 text-white font-semibold rounded-lg shadow hover:bg-gray-600 transition"
>
戻る
</button>
</div>
);
};
export default TaskDetailPage;
components/TaskDetailは以下の記述
import React from 'react';
const TaskDetail = ({ task }) => {
if (!task) return <p>Task not found</p>;
return (
<div>
<h2>{task.title}</h2>
<p>{task.description}</p>
</div>
);
};
export default TaskDetail;
大本
いかにgithubリンクを載せたので大本確認したい人はどうぞ