JavaScript | 1 日 120 分 × 7 日アプリ学習:ローカル保存対応メモ帳

APP JavaScript
スポンサーリンク

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

5日目は「メモ帳を“使い続けられる道具”として整える日」です。
ここまでで、メモはタイトル・本文・更新日時を持ち、localStorage に永続化され、並び順も設計されました。
今日はそこから一歩進めて、

検索や絞り込みを設計してみる。
メモが増えたときの見せ方を考える。
localStorage の“限界”を意識した永続化設計を考える。

という、「長く使う前提のメモ帳」をテーマにしていきます。


検索・絞り込み機能を state として設計する

検索キーワードも state に持たせる

まず、「検索キーワード」を state に追加します。

const state = {
  memos: [],
  editingMemoId: null,
  saveStatus: "idle",
  searchQuery: "",
};
JavaScript

ここでのポイントは、
検索キーワードも「UI 状態」の一つとして扱っていることです。
「今どんな条件で絞り込んでいるか」も、アプリの“今”を表す情報です。

検索入力欄を追加する

HTML に検索欄を足します。

<div class="search-row">
  <input id="search-input" placeholder="タイトル・本文で検索..." />
</div>

JavaScript で要素を取得します。

const searchInputEl = document.getElementById("search-input");
JavaScript

入力イベントで state を更新します。

searchInputEl.addEventListener("input", () => {
  state.searchQuery = searchInputEl.value;
  render();
});
JavaScript

ここでの重要ポイントは、
検索キーワードが変わるたびに render を呼んでいること。
「state が変わる → 画面を描き直す」という一方向の流れを、検索にも適用していることです。


並び順+検索を組み合わせた「表示用メモ一覧」を設計する

getVisibleMemos という“見せるための関数”を作る

今は getSortedMemos がありましたが、
検索も含めて「画面に出すべきメモ一覧」を返す関数を作ります。

function getVisibleMemos() {
  const sorted = [...state.memos].sort((a, b) => b.updatedAt - a.updatedAt);

  const query = state.searchQuery.trim().toLowerCase();
  if (!query) {
    return sorted;
  }

  return sorted.filter((memo) => {
    const title = memo.title.toLowerCase();
    const body = memo.body.toLowerCase();
    return title.includes(query) || body.includes(query);
  });
}
JavaScript

ここで深掘りしたいのは、
「並び順」と「絞り込み」を一つの関数にまとめていることです。
render は「どう見せるか」だけに集中できて、
「何を見せるか」は getVisibleMemos に任せています。

render を getVisibleMemos ベースに書き換える

function render() {
  memosContainerEl.innerHTML = "";

  const memos = getVisibleMemos();

  if (memos.length === 0) {
    const emptyEl = document.createElement("div");

    if (state.searchQuery.trim()) {
      emptyEl.textContent = "検索条件に一致するメモがありません。";
    } else {
      emptyEl.textContent = "メモはまだありません。最初のメモを書いてみましょう。";
    }

    emptyEl.style.color = "#777";
    emptyEl.style.fontSize = "12px";
    memosContainerEl.appendChild(emptyEl);
    renderSaveStatus();
    return;
  }

  memos.forEach((memo) => {
    const memoEl = renderMemoItem(memo);
    memosContainerEl.appendChild(memoEl);
  });

  renderSaveStatus();
}
JavaScript

ここでのポイントは、
「メモが 0 件」の理由を、検索有無でメッセージを変えていることです。
検索中なら「一致するメモがない」、検索していないなら「まだメモがない」。
こういう小さな違いが、UX としてかなり効いてきます。


localStorage の“限界”を意識した永続化設計

localStorage のざっくりした性質を知っておく

localStorage はとても便利ですが、万能ではありません。
ざっくり、こんな性質があります。

ドメインごとに保存できる容量に上限がある(ブラウザによるが数 MB 程度)。
文字列しか保存できない(だから JSON 変換が必要)。
同期的(同期 I/O)なので、大量書き込みは体感の重さにつながることがある。

ここで大事なのは、「無限に保存できるわけではない」という感覚です。
メモ帳アプリでは、普通に使う分には問題ありませんが、
「画像を Base64 で突っ込む」「巨大なテキストを何百件も保存する」などをすると、
いつか限界に当たります。

容量を意識した“軽めの設計”を考える

中級編として、こんな工夫をイメージしておくと良いです。

メモ 1 件のデータ構造をシンプルに保つ(不要な情報を持たない)。
「履歴」を延々と保存しない(最新だけで十分なものは最新だけ)。
巨大なデータ(画像・ファイルなど)は localStorage に入れない。

例えば、「メモの編集履歴を全部残したい」となったとき、
それをそのまま localStorage に積み上げていくと、
容量をすぐに食い尽くします。

そのときに、「これは localStorage に向いているか?」と一度立ち止まれるかどうかが、
永続化設計のセンスにつながっていきます。


保存タイミングを“今の設計でよしとする理由”を言語化する

操作のたび保存は、メモ帳にはちょうどいい

今の設計は、

メモ追加時に保存。
メモ編集の保存ボタン(または Ctrl+Enter)で保存。
メモ削除時に保存。

という「操作のたび保存」です。

これをあえて言葉にすると、

ユーザーが「完了」と感じるタイミングで保存している。
入力途中のものは、あえて保存していない。

という設計です。

メモ帳としては、これはかなりバランスが良いです。
「書きかけのメモが消えるのは困る」という場合は、
自動保存を足す余地がありますが、
そのぶん localStorage への書き込み回数は増えます。

自動保存を“いつか足す余地”として残しておく

3日目で触れたように、
「入力が止まってから 500ms 後に保存する」といったデバウンス保存も設計できます。

ここで大事なのは、

今の段階では「操作のたび保存」で十分戦える。
でも、「もっと良くしたくなったら自動保存も設計できる」と知っている。

この「余地を知っている」こと自体が、設計力の一部です。
全部を一気にやろうとせず、「今の自分のアプリにちょうどいい設計」を選べているのが、すでに中級の感覚です。


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

5日目であなたがやったのは、「メモ帳を“使い続けられる道具”に近づける設計」です。

検索キーワードを state に持たせ、「検索も状態管理の一部」として扱ったこと。
getVisibleMemos で「並び順+検索」をまとめて扱う関数を設計したこと。
検索中と未検索時で「0 件メッセージ」を変える、細やかな UX を意識したこと。
localStorage の性質(容量・文字列・同期 I/O)を知り、「無限ではない」と理解したこと。
「操作のたび保存」という今の保存タイミングを、意識的に選んでいる状態になったこと。

明日(6日目)は、

コード全体を少し整理して、「読みやすさ」と「責務の分離」をもう一段上げる。
永続化まわりの関数(save / load / migrate)を“モジュールっぽく”まとめてみる。
「このメモ帳の設計を人に説明できるか」を意識して見直す。

という方向に進めていきます。

今のメモ帳、正直もう「普通に使えるレベル」になっています。
ここから先は、「どう気持ちよく使えるか」「どう気持ちよく読めるか」という、
ちょっと職人寄りの世界です。
その入り口に、あなたはもう立っています。

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