はじめに
フロントエンド学習として、React + Viteでタスク管理アプリ「タスク帳」を作りました。
CRUD(作成・読み取り・更新・削除)とLocalStorageを使ったデータ保存という、Webアプリ開発の基本セットを一通り経験することを目的としました。
この記事では、何を作り、実装のどこにこだわったのか、そしてなぜそう作ったのかをまとめます。
GitURL
技術スタック
| 項目 | 使用technology |
|---|---|
| フレームワーク | React 18 |
| ビルドツール | Vite |
| データ保存 | ブラウザの localStorage |
| スタイル | 素のCSS(外部UIライブラリなし) |
サーバーは使わず、ブラウザだけで完結する構成にしています。バックエンドを用意しなくても
「動くアプリ」を体験できる、学習用途に向いた最小構成です。
できること
- タスクの追加・完了/未完了の切り替え・削除
- 「すべて/未完了/完了」でのフィルター表示
- 未完了タスクの件数表示
- LocalStorageへの自動保存(ページを閉じても消えない)
見た目は「ノート」をモチーフにしていて、タスクを完了させると判子(はんこ)が押されるような演出を入れました。地味な機能にも少し遊び心を入れると、作っていて楽しくなります。
実装のポイント
コンポーネント構成
App.jsx … 状態管理とCRUD関数の集約
├── TaskForm.jsx … タスク追加フォーム
├── TaskList.jsx … 一覧の表示
└── TaskItem.jsx … タスク1件分の表示
状態(tasks配列、filterの値)はすべてApp.jsxに集約し、子コンポーネントは、propsで受け取った値を表示し、操作があれば親から渡された関数を呼ぶだけにしています。
これは「状態を持ち上げる(Lifting State Up)」というReactの定石で、「今どのタスクが完了しているか」を複数の場所がバラバラに管理してしまうとバグの温床になるため、データの本体を1箇所にまとめることを徹底しました。
自動保存の仕組み
useEffect(() => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
}, [tasks]);
tasksが変化するたびにuseEffectが発火し、自動的にLocalStorageへ保存されます。
保存ボタンを用意する必要がなく、「編集する→自動で保存される」という
ユーザー体験をシンプルなコードで実現できるのがReactの気持ちいいところです。
設計判断で意識したこと
IDの生成は Date.now() にした
タスクのIDには、現在時刻(ミリ秒)であるDate.now()をそのまま使いました。
理論上は同じミリ秒に2件作られるとIDが重複するリスクがありますが、「ボタンを手で1件ずつ押して追加する」という今回のUIでは実害がほぼ無いため、シンプルさを優先しています。この判断は、後に作ったメモ帳アプリでは変えることになるのですが、
それはまた別の記事で触れます。
外部UIライブラリを使わなかった
ボタンや入力欄のスタイルは、すべて素のCSSで書いています。
Material UIのようなコンポーネントライブラリを使えばもっと早く見た目を整えられますが、今回は「CSSの基本(変数、疑似要素的な演出、レスポンシブ対応)を自分の手で書く」ことを優先しました。ライブラリに頼らずに一通り書いてみると、後でライブラリを使うときにも「中で何が起きているか」の見当がつきやすくなります。
つまずいたところ
- 配列やオブジェクトを直接書き換えず、常に新しい配列・オブジェクトを作ってstateを更新する、というReactのルールに最初は戸惑いました。慣れるまでは
mapやfilter、スプレッド構文(...)を使って不変に更新する感覚を意識的に確認しながら書いていました。 - CSSだけでノート風の罫線や判子の演出を作るのに、地味に時間がかかりました。
repeating-linear-gradientを使えば画像なしで罫線が表現できると知ったのは収穫でした。
まとめ
タスク管理アプリ自体はシンプルですが、CRUD・状態管理・LocalStorageという
フロントエンド開発の基本要素が全部詰まっていました。
次はこの構成をベースに、検索機能やMarkdown対応を加えた「メモ帳アプリ」に挑戦しました。
そちらは別記事で紹介します。