5日目のゴールと今日やること
5日目のテーマは
「fetch・async/await・エラーハンドリングの“型”をそのままに、ニュースアプリを“日常的に使えるレベル”へ引き上げる」
ことです。
ここまでであなたはすでに、
- キーワード検索
- 期間・並び順・ソース絞り込み
- ページネーション
- お気に入り登録
- ローディング表示
といった“ニュースアプリの基礎”を習得しました。
5日目はここに、
- 検索履歴の保存
- 最近見た記事の記録
- 状態管理の整理
- エラーハンドリングの再利用性アップ
を加えて、「毎日使えるニュースビューア」に近づけていきます。
検索履歴を実装する
なぜ履歴が必要なのか
ニュースアプリを使っていると、
「昨日検索したキーワードをもう一度見たい」
「よく検索するワードをすぐ呼び出したい」
という場面が必ず出てきます。
履歴は、ユーザーの操作回数を減らし、
アプリの“使いやすさ”を大きく向上させます。
履歴を配列で管理する
まずはメモリ上で履歴を管理します。
const history = [];
const historyDiv = document.getElementById("history");
JavaScript検索成功時に履歴へ追加します。
function addHistory(keyword) {
if (history.includes(keyword)) {
return;
}
history.unshift(keyword);
if (history.length > 5) {
history.pop();
}
renderHistory();
}
JavaScriptここでのポイントは、
- 重複を避ける
- 新しいものを先頭に入れる
- 最大件数を決めておく
という“履歴の基本ルール”を守ることです。
履歴を画面に表示する
function renderHistory() {
if (history.length === 0) {
historyDiv.textContent = "検索履歴はまだありません。";
return;
}
let html = "<h3>検索履歴</h3>";
history.forEach((keyword) => {
html += `<button class="history-item" data-keyword="${keyword}">${keyword}</button>`;
});
historyDiv.innerHTML = html;
const buttons = historyDiv.querySelectorAll(".history-item");
buttons.forEach((btn) => {
btn.addEventListener("click", () => {
const keyword = btn.dataset.keyword;
keywordInput.value = keyword;
currentKeyword = keyword;
currentPage = 1;
fetchNewsWithCurrentState();
});
});
}
JavaScript履歴ボタンを押すと、
そのキーワードで再検索できるようになります。
最近見た記事を記録する
「お気に入り」と「最近見た」は別物
お気に入りは「保存したい記事」。
最近見た記事は「直近で開いた記事」。
この 2 つは目的が違うため、
別の状態として管理します。
const recentArticles = [];
const recentDiv = document.getElementById("recent");
JavaScript記事を開いたときに記録する
記事リンクを押した瞬間に記録します。
function addRecent(article) {
const exists = recentArticles.some((a) => a.url === article.url);
if (!exists) {
recentArticles.unshift(article);
if (recentArticles.length > 5) {
recentArticles.pop();
}
}
renderRecent();
}
JavaScript記事一覧に「閲覧記録」を仕込む
renderArticles のリンク部分を少し変更します。
<p>
<a href="${url}" target="_blank" rel="noopener noreferrer" class="article-link" data-index="${index}">
記事を読む
</a>
</p>
リンククリック時に記録します。
const links = resultDiv.querySelectorAll(".article-link");
links.forEach((link, index) => {
link.addEventListener("click", () => {
addRecent(articles[index]);
});
});
JavaScript最近見た記事を表示する
function renderRecent() {
if (recentArticles.length === 0) {
recentDiv.textContent = "最近見た記事はありません。";
return;
}
let html = "<h3>最近見た記事</h3>";
recentArticles.forEach((article) => {
html += `
<div class="recent-item">
<p>${article.title}</p>
<a href="${article.url}" target="_blank" rel="noopener noreferrer">もう一度読む</a>
</div>
`;
});
recentDiv.innerHTML = html;
}
JavaScript状態管理を整理して「迷子にならないコード」にする
状態を一箇所にまとめる
状態が増えてきたので、
オブジェクトにまとめると見通しが良くなります。
const state = {
keyword: "",
from: "",
to: "",
sortBy: "publishedAt",
source: "",
page: 1,
pageSize: 10,
isLoading: false
};
JavaScript状態を更新する関数を作る
function updateState(updates) {
Object.assign(state, updates);
}
JavaScriptfetchNewsWithCurrentState を状態ベースに書き換える
async function fetchNewsWithCurrentState() {
if (state.isLoading) return;
startLoading();
showLoadingStatus();
renderLoadingSkeleton();
try {
const url = buildUrl(state);
const response = await fetch(url);
if (!response.ok) {
showError(`サーバーエラー(${response.status})`);
return;
}
const data = await response.json();
if (data.status === "error") {
showError(`エラー:${data.message}`);
return;
}
showStatus(`ニュース取得成功(ページ ${state.page})`);
renderArticles(data);
updatePaginationInfo(data);
addHistory(state.keyword);
} catch (error) {
showError(`ページ ${state.page} の取得に失敗しました。`);
console.error(error);
} finally {
endLoading();
}
}
JavaScript状態をまとめることで、
コードの読みやすさが一気に上がります。
エラーハンドリングを「再利用できる形」にする
エラー表示を一箇所に集約する
function handleError(error, context = "") {
if (context) {
showError(`${context}:${error.message || error}`);
} else {
showError(error.message || error);
}
}
JavaScriptfetchNewsWithCurrentState の catch を簡潔にする
} catch (error) {
handleError(error, `ページ ${state.page} の取得に失敗`);
}
JavaScriptこれで、
どんなエラーでも同じ形式で表示できます。
5日目のまとめ
今日やったことを整理すると、こうなります。
- fetch・async/await・エラーハンドリングの「型」はそのまま
- 検索履歴を追加して、再検索を簡単に
- 最近見た記事を記録して、読み返しやすく
- 状態管理をオブジェクトにまとめて、コードを整理
- エラーハンドリングを関数化して再利用性アップ
どれも「新しい文法」ではなく、
“状態管理 × DOM 更新 × 既存の fetch ロジック”
の組み合わせです。
今日いちばん深く理解してほしいこと
5日目の本質は、
「API 通信の型はもう完成している。
これからは“状態”をどう扱うかでアプリの質が決まる」
ということです。
履歴
最近見た記事
お気に入り
ページ番号
検索条件
これらはすべて「状態」です。
状態を整理し、
UI に反映するロジックを分離すると、
アプリは驚くほど読みやすく、拡張しやすくなります。
次の 6 日目では、
このニュースアプリに「複数キーワード検索」や
「検索条件のプリセット」などを追加して、
さらに“プロダクト寄り”の仕上がりにしていきます。

