JavaScript | 1 日 120 分 × 7 日アプリ学習:ToDoアプリ(設計力強化編)

JavaScript
スポンサーリンク

7日目のゴールと今日のテーマ

7日目は「この ToDo アプリを、自分の言葉で“設計として説明できるようになる日」です。
ここまでであなたは、

  • 状態管理(state)でアプリの“今”を表現し
  • 配列+オブジェクトでタスクをモデル化し
  • 再描画ロジック(render)で「state → 画面」の一方向を作り
  • 追加・完了・削除・フィルタ・編集まで設計してきました。

今日はそれらをまとめて、

  • 設計を“言語化”する
  • 「もし機能を足すなら?」を設計目線で考える
  • 自分が身につけたパターンを整理する

ところまで行きます。
コードを書くというより、「設計を説明できる自分」になる日です。


この ToDo アプリの「設計」を言葉で説明してみる

状態管理 state の設計

まず、アプリの中心にあるのが state です。

const state = {
  // ビジネスデータ:アプリが扱う全タスク
  tasks: [
    // { id: number, title: string, done: boolean }
  ],

  // UI 状態:どのフィルタが選ばれているか
  // "all" | "active" | "active" | "completed"
  filter: "all",

  // UI 状態:どのタスクが編集中か(なければ null)
  editingTaskId: null,
};
JavaScript

ここで大事なのは、

  • 「アプリが本当に扱いたいデータ」は tasks
  • 「画面の見せ方に関する状態」は filter と editingTaskId

という分離です。

この分離ができていると、

  • データの変更(タスク追加・完了・削除)
  • 表示の変更(フィルタ切り替え・編集モード)

を頭の中で整理して考えられます。

タスクのデータ構造

タスクはオブジェクトで表現しています。

{
  id: 1,
  title: "牛乳を買う",
  done: false,
}
JavaScript

ここに「期限」「優先度」「メモ」などを足しても、
設計として破綻しません。

  • id:一意な識別子(完了・削除・編集のターゲット)
  • title:ユーザーが見る名前
  • done:完了状態(フィルタの条件にも使う)

この「意味のあるプロパティ名を持つオブジェクトの配列」という形は、
ToDo に限らず、ほぼすべての CRUD アプリの基本パターンです。


再描画ロジックの設計:「state → 画面」の一方向

render の役割

render はアプリの「画面更新の司令塔」です。

function render() {
  renderTaskList();
  updateFilterButtons();
  updateTaskCount();
}
JavaScript

ここでのルールはシンプルです。

  • render は state を読んで画面を作り直すだけ
  • state を書き換えるのは、addTask / toggleTaskDone などの“状態変更関数”だけ

このルールを守ることで、

  • 「画面が変になるときは、state が変なはず」
  • 「state が正しければ、render は正しい画面を作るはず」

という前提が成り立ちます。
デバッグするときの“心の支え”になります。

タスク一覧の描画

function renderTaskList() {
  tasksContainerEl.innerHTML = "";

  const visibleTasks = getVisibleTasks();

  if (visibleTasks.length === 0) {
    const emptyEl = document.createElement("div");
    emptyEl.textContent = "タスクがありません。新しいタスクを追加してみましょう。";
    emptyEl.style.color = "#777";
    emptyEl.style.fontSize = "12px";
    tasksContainerEl.appendChild(emptyEl);
    return;
  }

  visibleTasks.forEach((task) => {
    const rowEl = renderTaskRow(task);
    tasksContainerEl.appendChild(rowEl);
  });
}
JavaScript

ここでのポイントは、

  • 「何を表示するか」は getVisibleTasks が決める
  • 「どう表示するか」は renderTaskRow が決める

という役割分担です。


フィルタと配列操作の設計

フィルタ条件をオブジェクトで持つ

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

ここでの設計のキモは、

  • フィルタ条件を if 文でベタ書きしない
  • 「名前付きの条件」として filters に集約する

というところです。

新しいフィルタ(例:高優先度だけ)を追加したくなったら、

filters.highPriority = (task) => task.priority === "high";

と書いて、ボタンを 1 つ足せば済みます。
既存のロジックを壊さずに拡張できる——これが「設計が強い」ということです。

追加・更新・削除の配列パターン

タスク追加:

function addTask(title) {
  const trimmed = title.trim();
  if (!trimmed) return;

  const newTask = {
    id: nextId++,
    title: trimmed,
    done: false,
  };

  state.tasks.push(newTask);
  render();
}
JavaScript

完了切り替え(イミュータブル寄り):

function toggleTaskDone(id) {
  state.tasks = state.tasks.map((task) => {
    if (task.id !== id) return task;
    return { ...task, done: !task.done };
  });
  render();
}
JavaScript

削除:

function deleteTask(id) {
  state.tasks = state.tasks.filter((task) => task.id !== id);
  render();
}
JavaScript

ここであなたがもう持っている感覚は、

  • 追加=配列に 1 件足す
  • 更新=条件に一致する要素だけ中身を変えた新しい配列を作る
  • 削除=条件に一致しない要素だけ残した新しい配列を作る

という「配列操作の 3 パターン」です。
これは今後、どんなアプリを作るときにも使い回せる“武器”になります。


編集機能と「モード管理」の設計

編集モードを state で持つ

state.editingTaskId = null;
JavaScript

編集開始:

function startEditTask(id) {
  state.editingTaskId = id;
  render();
}
JavaScript

編集終了:

function finishEditTask() {
  state.editingTaskId = null;
  render();
}
JavaScript

ここでの本質は、

  • 「今どのタスクが編集モードか」も state の一部
  • 行側は「自分の id と editingTaskId が一致しているか」で表示を切り替える

という構造です。

行の表示モードと編集モード

function renderTaskRow(task) {
  const rowEl = document.createElement("div");
  rowEl.className = "task-item";

  const isEditing = state.editingTaskId === task.id;

  if (isEditing) {
    renderTaskRowEditing(rowEl, task);
  } else {
    renderTaskRowDisplay(rowEl, task);
  }

  return rowEl;
}
JavaScript

この「モードを state で管理し、描画側はそれに従うだけ」というパターンは、
フォームバリデーション編でも出てきた、かなり重要な設計パターンです。


「もし機能を足すなら?」を設計目線で考える

例1:優先度を追加するなら

やりたいこと:

  • タスクに priority(”high” | “medium” | “low”)を持たせる
  • 「高優先度だけ表示」フィルタを追加する

設計として必要な変更は、

  • タスクオブジェクトに priority を追加
  • 追加時の newTask に priority: "medium" などを入れる
  • filters に highPriority 条件を追加
  • フィルタボタンを 1 つ追加(data-filter=”highPriority”)

これだけで済みます。
state の構造も、render の構造も壊れません。

例2:期限切れタスクを赤く表示するなら

やりたいこと:

  • タスクに dueDate を持たせる
  • 今日より前のタスクは赤字で表示する

設計としては、

  • タスクオブジェクトに dueDate を追加
  • renderTaskRowDisplay の中で「期限切れかどうか」を判定してクラスを付ける

だけで済みます。
state の構造はそのまま、
「見せ方」だけを変えればいい設計になっています。

こうやって「機能追加を頭の中でシミュレーションしてみて、
既存の設計がどれだけ耐えられそうか」を考えるのが、
設計力を育てる一番のトレーニングです。


7日目のまとめ:あなたがこの 7 日間で手に入れたもの

ToDo アプリ【設計力強化編】で、あなたが身につけたのは単なる「ToDo の作り方」ではありません。

  • アプリの“今”を表現する state の設計
  • 配列+オブジェクトでデータをモデル化する感覚
  • 「state → 画面」の一方向データフロー
  • 追加・更新・削除の配列操作パターン
  • フィルタを条件オブジェクトとして持つ設計
  • モード(編集中など)を state で管理する考え方
  • render を「何度呼んでも安全な読み取り専用の関数」として扱う姿勢

これらは全部、
フォームバリデーションアプリともつながっているし、
今後作るどんなアプリにもそのまま持ち込める“設計の型”です。

ここから先は、

  • この ToDo を自分なりに改造してみる
  • 別の題材(メモ帳、買い物リスト、習慣トラッカー)で同じ設計を試す
  • React / Vue などのフレームワークで「同じことをコンポーネントでやる」

みたいな方向に進めます。

最後に一つだけ聞きたいのは、

「この ToDo アプリ、どこが一番“自分の好きな設計”だと感じた?」

そこに、あなたのセンスが一番強く出ています。
そこを意識して伸ばしていくと、設計はどんどん自分のものになっていきます。

タイトルとURLをコピーしました