5日目のゴールと今日のテーマ
5日目は「ここまで作ってきた ToDo アプリの設計を、一度“俯瞰して整える日”」です。
機能はすでにかなり揃っていますが、今日は
- 状態管理(state)の整理
- データ構造(配列+オブジェクト)の見直し
- 再描画ロジックの責務の明確化
- 「壊れにくい設計」になっているかのチェック
を意識して、コードを“設計目線”で見直していきます。
新機能をガンガン足すというより、「土台を固める」日です。
現在の設計を言葉で整理してみる
今の state の構造を再確認する
ここまでで、state はだいたいこんな形になっているはずです。
const state = {
tasks: [
// { id, title, done }
],
filter: "all", // "all" | "active" | "completed"
editingTaskId: null, // 編集中のタスク id(なければ null)
};
JavaScriptここで意識してほしいのは、「役割ごとに分けて見られるか」です。
データそのものは tasks。
表示の状態は filter と editingTaskId。
この区別ができていると、
「これはビジネスロジック」「これは UI ロジック」という線引きがしやすくなります。
タスクのデータ構造をもう一度眺める
タスクはこうでした。
{
id: 1,
title: "牛乳を買う",
done: false,
}
JavaScriptここで考えてみてほしいのは、「今後何を足せそうか」です。
例えば、期限(dueDate)、優先度(priority)、メモ(note)など。
そう考えると、「オブジェクトで持つ」という選択が
どれだけ拡張に強いかが見えてきます。
「状態を変える関数」と「画面を描く関数」をはっきり分ける
状態を変える関数たちを一覧にしてみる
今のコードには、こんな関数があるはずです。
function addTask(title) { ... }
function toggleTaskDone(id) { ... }
function deleteTask(id) { ... }
function startEditTask(id) { ... }
function finishEditTask() { ... }
function updateTaskTitle(id, newTitle) { ... }
JavaScriptこれらはすべて「state を変える」役割を持っています。
共通しているのは、「最後に render を呼ぶ」こと。
ここで一度、「どこで render を呼ぶか」を整理してみます。
例えば、
- state を変える関数の中で必ず render を呼ぶ
か - state を変える関数は render を呼ばず、呼ぶのはイベント側に統一する
どちらもアリです。
5日目では、前者(state を変える関数の中で render)を採用しつつ、
「一貫しているか」を確認してみましょう。
render の責務をもう一度確認する
render はこうなっているはずです。
function render() {
renderTaskList();
updateFilterButtons();
updateTaskCount();
}
JavaScriptここで大事なのは、
「render は state を一切変えない」
というルールです。
render は「読むだけ」、書き換えるのは state を扱う関数だけ。
この線引きができていると、バグの原因がかなり絞りやすくなります。
getVisibleTasks を“設計目線”で見直す
フィルタロジックを関数として独立させる意味
今の getVisibleTasks はこうでした。
function getVisibleTasks() {
if (state.filter === "all") {
return state.tasks;
}
if (state.filter === "active") {
return state.tasks.filter((task) => !task.done);
}
if (state.filter === "completed") {
return state.tasks.filter((task) => task.done);
}
return state.tasks;
}
JavaScriptここで意識してほしいのは、
「表示に関するロジックが、state.tasks の外側にある」
ということです。
tasks 自体は「全タスク」。
getVisibleTasks は「今の filter に応じた見せ方」。
この分離ができていると、
「データをどう持つか」と「どう見せるか」が混ざりません。
フィルタ条件をオブジェクトにしてみる
設計力強化編なので、もう一歩だけ踏み込みます。
const filters = {
all: (task) => true,
active: (task) => !task.done,
completed: (task) => task.done,
};
function getVisibleTasks() {
const predicate = filters[state.filter] || filters.all;
return state.tasks.filter(predicate);
}
JavaScriptこうすると、
- フィルタ条件が「名前付きの関数」として整理される
- フィルタを増やしたいときは filters に追加するだけで済む
という設計になります。
「条件を if 文でベタ書きする」のではなく、
「条件をデータ(オブジェクト)として持つ」という発想は、
中級から上級に上がるときの大きな一歩です。
タスク操作ロジックを“配列操作”の観点から整理する
追加・更新・削除を「配列操作」として眺める
タスク追加は push。
完了切り替えは find してプロパティ変更。
削除は filter で「残すものだけ残す」。
これをあえて言葉にすると、
追加は「末尾に 1 件足す」。
更新は「ある条件に一致する要素を 1 件探して、中身を書き換える」。
削除は「ある条件に一致しない要素だけを残す新しい配列を作る」。
この「配列操作のパターン」を意識しておくと、
他のアプリでも同じ考え方を使い回せます。
更新を“イミュータブル風”に書いてみる
設計力強化として、更新を少しだけ書き換えてみます。
function toggleTaskDone(id) {
state.tasks = state.tasks.map((task) => {
if (task.id !== id) return task;
return {
...task,
done: !task.done,
};
});
render();
}
JavaScriptこうすると、
- 元の配列を直接いじらず、新しい配列を作っている
- 更新されたタスクも「新しいオブジェクト」として扱われる
という、いわゆる「イミュータブル寄り」の書き方になります。
必須ではありませんが、
React などのフレームワークに進むときに、
この感覚がかなり効いてきます。
「壊れにくい設計」になっているかをチェックする
機能追加を想像してみる
例えば、次の機能を足したくなったとします。
優先度(高・中・低)をタスクに持たせて、
「高優先度だけ表示」というフィルタを追加する。
このときに、
タスクのデータ構造に priority を足す。
フィルタ条件 filters に highPriority を足す。
フィルタボタンを 1 つ追加する。
これだけで済みそうか?
既存の関数を大きく書き換えなくてもいけそうか?
もし「いけそう」と感じるなら、
今の設計はかなり“拡張に強い”状態になっています。
逆に「怪しいところ」を探してみる
例えば、
- render の中で state を書き換えていないか
- 1 つの関数があまりに多くのことをしていないか
- 命名があいまいで、「何をしている関数か」一瞬でわからないところはないか
こういう視点で、自分のコードを疑ってみるのも
設計力を上げるうえで大事な時間です。
5日目のまとめと、明日へのつなぎ
5日目は、機能追加よりも「設計を眺め直す」ことに時間を使いました。
あなたが今日やったのは、
state を「データ」と「表示状態」に分けて意識し直すこと。
render を「読むだけの関数」として位置づけ直すこと。
getVisibleTasks をフィルタ条件オブジェクトで整理すること。
配列操作(追加・更新・削除)をパターンとして捉え直すこと。
「この設計で機能を増やしても壊れないか?」を自分でチェックすること。
これらは全部、「中級から上に行く人」が必ず通る視点です。
明日(6日目)は、ここからさらに一歩進めて、
小さなリファクタリングを実際に手を動かしてやってみる。
命名や関数分割を変えたときに、読みやすさがどう変わるかを体感する。
「他人が読んでもわかる ToDo アプリ」に近づける。
そんな方向に進めていきます。
もし余裕があれば、今日はあえてコードを書き換えずに、
自分のコードを「設計図」として眺めてみてください。
どこが好きで、どこがモヤっとするか——そこに、あなたの次の伸びしろがあります。

