4日目のゴールと今日やること
4日目のテーマは
「Datamuse API アプリを“毎日使えるツール”に近づけるために、状態管理・お気に入り・ローカルストレージ保存を導入する」
ことです。
ここまでであなたは、
- fetch で API からデータを取得
- async/await で読みやすい非同期処理
- try-catch でエラーを扱う
- サジェスト(sp=)
- 検索履歴
を実装してきました。
4日目は、アプリとしての完成度を一気に上げるために、
- 状態管理(state)
- お気に入り機能
- localStorage 保存
- ローディング表示の強化
- エラーハンドリングの整理
を行います。
今日の内容は、アプリ開発の“中級者の壁”を越えるための重要ポイントが詰まっています。
状態管理(state)を導入する
なぜ state が必要なのか
昨日までのコードでは、
履歴・サジェスト・検索結果などがバラバラの変数で管理されていました。
これをひとつのオブジェクトにまとめると、
- どこに何があるか迷わない
- UI 更新がしやすい
- 保存(localStorage)と連動しやすい
- デバッグがしやすい
というメリットがあります。
state の基本形
const state = {
history: [],
favorites: [],
isLoading: false
};
JavaScript状態を更新する関数
function updateState(updates) {
Object.assign(state, updates);
}
JavaScriptこれだけで、アプリ全体のコードがスッキリします。
お気に入り機能を追加する
お気に入りの構造を決める
お気に入りには、最低限これだけあれば十分です。
- 単語
- スコア
- モード(類義語 / 連想語 / 韻)
function addFavorite(word, score, mode) {
state.favorites.unshift({ word, score, mode });
if (state.favorites.length > 20) {
state.favorites.pop();
}
saveFavorites();
renderFavorites();
}
JavaScriptlocalStorage に保存する
保存の基本ルール
localStorage は 文字列しか保存できない ため、
配列やオブジェクトは JSON に変換します。
保存:
localStorage.setItem("dm_favorites", JSON.stringify(state.favorites));
JavaScript読み込み:
const saved = JSON.parse(localStorage.getItem("dm_favorites") || "[]");
JavaScript保存関数
function saveFavorites() {
localStorage.setItem("dm_favorites", JSON.stringify(state.favorites));
}
JavaScript読み込み関数
function loadFavorites() {
const saved = localStorage.getItem("dm_favorites");
if (!saved) {
state.favorites = [];
return;
}
try {
const parsed = JSON.parse(saved);
state.favorites = Array.isArray(parsed) ? parsed : [];
} catch (e) {
console.error("お気に入りの読み込みに失敗しました", e);
state.favorites = [];
}
renderFavorites();
}
JavaScriptお気に入りを UI に表示する
表示関数
function renderFavorites() {
if (state.favorites.length === 0) {
favoritesDiv.textContent = "お気に入りはまだありません。";
return;
}
let html = "<h3>お気に入り</h3>";
state.favorites.forEach((item, index) => {
html += `
<p class="fav-item" data-index="${index}">
${item.word}(スコア: ${item.score} / ${getModeLabel(item.mode)})
</p>
`;
});
favoritesDiv.innerHTML = html;
const items = favoritesDiv.querySelectorAll(".fav-item");
items.forEach((el) => {
el.addEventListener("click", () => {
const index = el.dataset.index;
const fav = state.favorites[index];
wordInput.value = fav.word;
modeSelect.value = fav.mode;
fetchWords();
});
});
}
JavaScript深掘りポイント
お気に入りをクリックすると、
入力欄に反映して再検索できる
という自然な動きになります。
これは、検索処理が fetchWords() に一本化されているからです。
ローディング表示を強化する
状態と UI を連動させる
function startLoading(message) {
updateState({ isLoading: true });
statusDiv.textContent = message || "取得中です…";
searchButton.disabled = true;
}
function endLoading() {
updateState({ isLoading: false });
searchButton.disabled = false;
}
JavaScript深掘りポイント
ローディング表示は、
「アプリが固まっていない」ことを伝える重要な UI
です。
状態(state)と UI を連動させることで、
アプリ全体の動きが統一されます。
エラーハンドリングを整理する
fetchWords の完成形(4日目版)
async function fetchWords() {
const word = wordInput.value.trim();
const mode = modeSelect.value;
if (!word) {
statusDiv.textContent = "単語を入力してください。";
resultDiv.textContent = "";
return;
}
const modeLabel = getModeLabel(mode);
startLoading(`${modeLabel}を取得中です…`);
resultDiv.textContent = "";
try {
const url = `https://api.datamuse.com/words?${mode}=${encodeURIComponent(word)}`;
const response = await fetch(url);
if (!response.ok) {
statusDiv.textContent = `サーバーエラーが発生しました。(${response.status})`;
return;
}
const data = await response.json();
if (!Array.isArray(data)) {
statusDiv.textContent = "予期しない形式のデータが返されました。";
return;
}
if (data.length === 0) {
statusDiv.textContent = `${modeLabel}が見つかりませんでした。`;
return;
}
statusDiv.textContent = `${modeLabel}の取得に成功しました。`;
const sorted = [...data].sort((a, b) => (b.score || 0) - (a.score || 0));
renderWords(sorted, modeLabel);
addHistory(word, mode);
} catch (error) {
statusDiv.textContent = "通信に失敗しました。ネットワークを確認してください。";
console.error(error);
} finally {
endLoading();
}
}
JavaScript4日目のまとめ
今日やったことを整理すると、こうなります。
- state にアプリの状態を集約
- お気に入り機能を追加
- localStorage に保存して永続化
- お気に入りから再検索できるように
- ローディング表示を状態と連動
- エラーハンドリングを整理
- fetchWords を「読みやすい流れ」に改善
どれも新しい文法ではなく、
「fetch / async-await / エラーハンドリングの型をどう設計に落とし込むか」
という話です。
今日いちばん深く理解してほしいこと
4日目の本質は、
「状態管理と保存を整えると、アプリは一気に“毎日使えるツール”になる」
ということです。
検索
サジェスト
履歴
お気に入り
ローディング
エラー表示
これらはすべて、
fetch / async-await / try-catch の型の上に乗っています。
5日目では、
UI の改善、検索体験の向上、状態管理の整理などを行い、
さらに完成度の高い Datamuse アプリに育てていきます。


