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

JavaScript
スポンサーリンク

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

6日目は「設計を“他人が読んでもわかるレベル”まで磨き込む日」です。
ここまでで ToDo アプリは十分動いていますが、今日は

  • 関数の分割と命名を見直して、読みやすさを上げる
  • 状態管理(state)の扱い方を、より一貫した形に整える
  • データ構造(配列+オブジェクト)の操作をパターンとして整理する
  • 「バグりにくい書き方」を意識してコードを微調整する

という、「リファクタリング寄りの 1 日」にします。
新機能は最小限にして、その代わり“設計の精度”を上げていきます。


状態管理を「読みやすい形」に整える

state を“1カ所で定義しきる”という意識

今の state はだいたいこうなっています。

const state = {
  tasks: [],
  filter: "all",
  editingTaskId: null,
};
JavaScript

ここで大事なのは、「state に何があるか」が一目でわかることです。
6日目では、ここにコメントを足して“設計書”としての役割も持たせます。

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

  // 現在の表示フィルタ(UI 状態)
  // "all" | "active" | "completed"
  filter: "all",

  // 現在編集中のタスク id(なければ null)
  // 編集モードの管理用(UI 状態)
  editingTaskId: null,
};
JavaScript

ここでのポイントは、
「これはビジネスデータ」「これは UI 状態」という区別を
コメントで明示しておくことです。
自分の未来の自分にも、他人にも優しい設計になります。


関数の役割を“名前だけで伝わるように”する

「何をするか」が一瞬でわかる名前にする

今ある関数をざっと並べてみます。

addTask
toggleTaskDone
deleteTask
startEditTask
finishEditTask
updateTaskTitle
getVisibleTasks
render
renderTaskList
renderTaskRow
renderTaskRowDisplay
renderTaskRowEditing
updateFilterButtons
updateTaskCount
JavaScript

6日目で意識したいのは、

  • 動詞+目的語で「何をするか」がわかるか
  • 「状態を変える関数」と「描画する関数」が名前から区別できるか

という点です。

例えば、getVisibleTasks は「見えるタスクを取得する」とわかるので良い名前です。
renderTaskRow は「1 行分の DOM を作る」とわかるので、これも良いです。

逆に、もし doEdithandleClick のような
「中身を見ないとわからない名前」があれば、
startEditTaskonFilterButtonClick のように
“意図が伝わる名前”に変えていきます。


イベントハンドラを“入口”として整理する

イベントハンドラとロジックを分ける

今のコードでは、イベントリスナーの中に
ロジックが直接書かれている部分があるかもしれません。

例えば:

addButtonEl.addEventListener("click", () => {
  addTask(taskInputEl.value);
  taskInputEl.value = "";
});
JavaScript

これは悪くないですが、6日目では
「イベントハンドラも関数として名前を付ける」ことをやってみます。

addButtonEl.addEventListener("click", onAddButtonClick);
taskInputEl.addEventListener("keydown", onTaskInputKeyDown);

function onAddButtonClick() {
  addTask(taskInputEl.value);
  taskInputEl.value = "";
}

function onTaskInputKeyDown(event) {
  if (event.key === "Enter") {
    addTask(taskInputEl.value);
    taskInputEl.value = "";
  }
}
JavaScript

ここでの重要ポイントは、

  • イベントハンドラは「UI の入口」
  • ビジネスロジック(addTask など)は「状態を変える役」

と役割を分けていることです。
これにより、「どこから state が変わるのか」が追いやすくなります。


配列操作を“パターン化して意識する”

追加・更新・削除をそれぞれパターンとして見る

タスク追加:

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 パターン”として頭に入れておくことです。

この 3 つのパターンを意識しておくと、
別のアプリ(メモ帳、ユーザー一覧、商品リストなど)でも
まったく同じ考え方で設計できます。


再描画ロジックを“安心して呼べるもの”にする

render は「副作用のない読み取り専用」に近づける

render の中身はこうでした。

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

ここで大事なのは、

  • render の中で state を書き換えない
  • render の中で「新しいイベントリスナーを無限に追加しない」

という 2 点です。

タスク行の描画でイベントリスナーを付けていますが、
毎回 innerHTML = "" で中身を消してから作り直しているので、
古い DOM とリスナーは破棄されます。
この構造なら、render を何度呼んでも「増殖」はしません。

6日目では、「render は何度呼んでも安全」という前提を
自分の中でしっかり意識しておきましょう。
これが崩れると、一気にバグりやすくなります。


小さなリファクタリングで“読みやすさ”を上げる

例:タスク行の描画を少しだけ整理する

今の renderTaskRowDisplay は、
タイトル・完了ボタン・編集ボタン・削除ボタンを
一気に組み立てています。

ここを、さらに小さく分けることもできます。

function renderTaskRowDisplay(rowEl, task) {
  const titleEl = createTaskTitleElement(task);
  const actionsEl = createTaskActionsElement(task);

  rowEl.appendChild(titleEl);
  rowEl.appendChild(actionsEl);
}

function createTaskTitleElement(task) {
  const titleEl = document.createElement("div");
  titleEl.className = "task-title";
  if (task.done) {
    titleEl.classList.add("done");
  }
  titleEl.textContent = task.title;
  return titleEl;
}

function createTaskActionsElement(task) {
  const actionsEl = document.createElement("div");
  actionsEl.className = "task-actions";

  const toggleButton = document.createElement("button");
  toggleButton.textContent = task.done ? "未完了に戻す" : "完了";
  toggleButton.addEventListener("click", () => {
    toggleTaskDone(task.id);
  });

  const editButton = document.createElement("button");
  editButton.textContent = "編集";
  editButton.addEventListener("click", () => {
    startEditTask(task.id);
  });

  const deleteButton = document.createElement("button");
  deleteButton.textContent = "削除";
  deleteButton.addEventListener("click", () => {
    deleteTask(task.id);
  });

  actionsEl.appendChild(toggleButton);
  actionsEl.appendChild(editButton);
  actionsEl.appendChild(deleteButton);

  return actionsEl;
}
JavaScript

ここでのポイントは、

  • 「1 関数 1 役割」に近づける
  • 関数名だけで「何を作っているか」がわかる

という読みやすさです。
もちろん、やりすぎると関数が増えすぎて逆に読みにくくなるので、
「自分が読みやすいバランス」を探すのも大事な経験です。


6日目のまとめと、明日へのつなぎ

6日目は、機能追加よりも「設計の精度を上げる」ことに集中しました。

あなたが今日やったのは、

state を“設計書”としてコメント付きで整理すること。
関数名と役割を見直して、「名前だけで意図が伝わる」形に近づけること。
イベントハンドラとビジネスロジックを分けて、入口をはっきりさせること。
配列操作(追加・更新・削除)を 3 パターンとして意識し直すこと。
render を「何度呼んでも安全な読み取り専用の関数」として扱うこと。

これは全部、「中級から上級に上がるための筋トレ」です。

明日(7日目)はいよいよ総仕上げとして、

この ToDo アプリを「自分の言葉で説明できる」ように整理する。
もし機能を 1 つ足すなら、どこをどう変えるかを設計してみる。
“設計力強化編”として、学んだパターンを振り返る。

という 1 日にしていきます。

今日は、コードを眺めながら
「この設計、けっこういいじゃん」と思えるところと、
「ここはまだモヤっとするな」というところを
自分なりに見つけてみてください。
その感覚が、次の一段上のあなたを連れてきます。

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