はじめに
React 100本ノックの2作品目です。
今回は、React + TypeScriptでシンプルなTodoアプリを作りました。
#001では学習記録アプリを作り、useStateやmap()などReactの基本的な使い方を学びました。
今回はそこから一歩進んで、
- Todoの追加
- Todoの一覧表示
- Todoの完了・未完了
- Todoの削除
- Todo件数の表示
を実装します。
特に、Reactで配列のデータを追加・更新・削除する方法を理解することを目的としました。
今回の目的
今回の目的は、単にTodoアプリを完成させることではありません。
以下のReactの基本を、実際にコードを書きながら理解することを目的としました。
useState
↓
データを管理する
map()
↓
データを一覧表示・更新する
filter()
↓
データを絞り込んで削除する
使用技術
- React
- TypeScript
- Vite
作ったもの
主な機能
- Todoを追加
- Todoを一覧表示
- Todoを完了にする
- Todoを未完了に戻す
- Todoを削除
- Todoの総数を表示
- 未完了のTodo数を表示
画面
ローカル環境構築
1. プロジェクト作成
npm create vite@latest react-002-todo-app -- --template react-ts
React + TypeScriptのプロジェクトをViteで作成します。
2. プロジェクトへ移動
cd react-002-todo-app
3. パッケージをインストール
npm install
4. 開発サーバーを起動
npm run dev
ブラウザから以下にアクセスします。
http://localhost:5173/
Todoのデータ構造
まず、Todoがどのようなデータなのかを定義しました。
type Todo = {
id: number;
title: string;
completed: boolean;
};
例えば、以下のようなデータになります。
{
id: 1,
title: "Reactを勉強する",
completed: false
}
それぞれの役割は以下の通りです。
| プロパティ | 意味 |
|---|---|
id |
Todoを識別するためのID |
title |
Todoの内容 |
completed |
Todoが完了しているか |
useStateでTodoを管理する
Todo一覧をuseStateで管理しました。
const [todos, setTodos] = useState<Todo[]>([]);
最初はTodoがないため、
[]
という空の配列です。
Todoが追加されると、
[]
↓
[Reactを勉強する]
↓
[Reactを勉強する, TypeScriptを勉強する]
のようにstateが変化していきます。
Reactではstateが変更されると、それに合わせて画面も更新されます。
Todoを追加する
Todoの追加処理は以下のようにしました。
const handleAddTodo = () => {
if (!title.trim()) {
return;
}
const newTodo: Todo = {
id: Date.now(),
title: title,
completed: false,
};
setTodos([...todos, newTodo]);
setTitle("");
};
ここで重要なのが、
setTodos([...todos, newTodo]);
です。
例えば現在、
[A, B]
というTodoがあるとします。
そこにCを追加すると、
[A, B, C]
になります。
...todosによって現在のTodoを展開し、その後ろにnewTodoを追加しています。
Todoを一覧表示する
Todoの表示にはmap()を使いました。
{todos.map((todo) => (
<li key={todo.id}>
{todo.title}
</li>
))}
map()は配列のデータを1つずつ取り出して処理できます。
例えば、
[A, B, C]
という配列があれば、
A
B
C
と1つずつ処理できます。
Todoアプリでは、
todos
↓
map()
↓
Todoを1件ずつ取り出す
↓
画面に表示
という流れです。
Todoを完了・未完了にする
次にTodoの状態を変更します。
const handleToggleTodo = (id: number) => {
setTodos(
todos.map((todo) =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
)
);
};
少し難しい処理ですが、やっていることはシンプルです。
Todoを1件ずつ確認
↓
変更したいTodo?
↙ ↘
YES NO
↓ ↓
状態を変更 そのまま
例えば、
completed: false
だった場合、
!todo.completed
によって、
false → true
になります。
もう一度押せば、
true → false
となります。
Todoを削除する
削除にはfilter()を使いました。
const handleDeleteTodo = (id: number) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
例えば、
[A, B, C]
からBを削除したい場合、
A → 残す
B → 削除
C → 残す
となり、
[A, C]
という新しい配列になります。
今回のポイントは、
todo.id !== id
です。
つまり、
「削除したいIDではないTodoだけ残す」
という処理になっています。
Todoの件数を表示する
Todoの総数は、
todos.length
で取得できます。
<p>全て:{todos.length}件</p>
さらに、未完了のTodoだけを取得するためにfilter()を使いました。
const incompleteCount = todos.filter(
(todo) => !todo.completed
).length;
これによって、
Todo 3件
↓
完了 1件
↓
未完了 2件
というように未完了の件数を表示できます。
🏐 バレーボールに例えて理解する
今回のReactの処理をバレーボールに例えてみました。
Todoを追加
新しい選手がチームに入るイメージです。
選手A
選手B
↓
選手A
選手B
選手C
setTodos([...todos, newTodo]);
Todoを更新
選手の状態を変更するイメージです。
ベンチ
↓
試合に出る
map()
Todoを削除
チームから選手を外すイメージです。
選手A
選手B ← 外す
選手C
↓
選手A
選手C
filter()
このように考えると、
追加 → [...todos]
表示 → map()
更新 → map()
削除 → filter()
という役割の違いをイメージしやすくなりました。
CRUDについて
今回のTodoアプリでは、CRUDの基本も体験しました。
| 操作 | CRUD | Todoアプリ |
|---|---|---|
| 作成 | Create | Todoを追加 |
| 取得 | Read | Todoを表示 |
| 更新 | Update | 完了・未完了を変更 |
| 削除 | Delete | Todoを削除 |
まだデータベースやAPIは使っていませんが、
「データを作る・見る・変更する・削除する」
という基本的な考え方をReact上で体験できました。
#001から何が変わったか
#001では、
入力
↓
state
↓
表示
というReactの基本的な流れを学びました。
#002では、
入力
↓
state
↓
追加
↓
表示
↓
更新
↓
削除
まで扱いました。
特に今回、
map()
と
filter()
を実際に使ってみたことで、Reactで配列を操作するイメージが少し掴めました。
今回の学び
今回のTodoアプリを作ってみて、特に理解したかったのは、
Reactではstateを変更すると、その変更に合わせて画面が更新される
という考え方です。
例えば、
Todoを追加
↓
setTodos()
↓
todosが変わる
↓
Reactが再描画
↓
Todo一覧が変わる
という流れになります。
単純なTodoアプリですが、Reactの基本的な考え方を理解するには良い題材だと感じました。
次回
次回は、React 100本ノック #003に進みます。
#003では、今回学んだfilter()をさらに発展させて、
商品一覧
↓
検索
↓
カテゴリ絞り込み
↓
検索結果表示
という商品検索アプリを作る予定です。
📚 React 100本ノック
| 回 | 作成物 | 学習内容 |
|---|---|---|
| #001 | 学習記録アプリ |
useState / map / reduce
|
| #002 | Todo App | useState / map / filter / CRUD |
| #003 | 商品検索アプリ | 検索 / 絞り込み |
| #004〜 | Coming Soon | Coming Soon |
🔗 GitHub
GitHubリポジトリ:
https://github.com/spring46-stack/react-002-todo-app
まとめ
今回はTodoアプリを作りながら、
useState
map()
filter()
CRUD
について学びました。
特に、
追加 → [...todos]
表示 → map()
更新 → map()
削除 → filter()
という使い分けを実際にコードを書きながら理解できたのが今回の一番の収穫でした。
React 100本ノック、2/100完了です。
