つくる 作品データを API から読む

このレッスンで分かること

  • 通信、待ちの表示、失敗の表示、絞り込み、並び替えを 1 枚にまとめられる
  • データの流れを「取る」「選ぶ」「描く」の 3 段に分けて書ける
  • 手で書いた配列を消しても、同じ画面が出せると確かめられる
  • 作品ギャラリーが、外のデータで動く状態まで進む

つくる 作品データを API から読む とは

第9章でやってきたことを、ポートフォリオの作品ギャラリーに全部つなぎます。手で書いていた作品の配列を消し、API から取ってきたデータで同じ画面を出します。

ここまでで何ができるようになったか

第9章の 4 本で、次のことを覚えました。

覚えたこと使う場所
fetch と 2 回の awaitデータを取ってくる入口
?delay=800 で待ちを再現する読み込み中の表示を確かめる
response.okthrow失敗を自分で判定する
catch で画面に出す利用者に失敗を伝える
JSON.stringify と送り先の決めかた送る側の組み立て

そしてもっと大事なことがひとつあります。描画のコードは何も変わらなかったという事実です。第8章で書いた renderWorks は、配列をもらってカードを並べるだけの関数でした。だから、配列の出どころが手書きから通信に変わっても、書き直す必要がありませんでした。

「変わるところ」と「変わらないところ」を分けて書いておくと、あとから来る変更に強くなります。今回は出どころだけが変わりました。

取る、選ぶ、描く の 3 段に分ける

この回で書くコードは、次の 3 つの役割に分かれます。ひとつの関数に詰め込まず、分けたまま組み立ててください。

// 1. 取る。通信のことだけ知っている async function loadWorks() { /* ... */ } // 2. 選ぶ。配列をもらって配列を返すだけ function selectWorks(works) { /* ... */ } // 3. 描く。配列をもらって画面を書き換えるだけ function renderWorks(works) { /* ... */ }

真ん中の「選ぶ」が今回の新顔です。公開されている作品だけを残し、表示回数の多い順に並べます。第2章で書いた filtersort がそのまま使えます。

function selectWorks(works) { return works .filter((work) => work.published) .sort((a, b) => b.views - a.views); }

sort は元の配列を並べ替えてしまう破壊的なメソッドですが、filter が新しい配列を作ったあとなので、ここでは元のデータに影響しません。filter を先に置くのは、見た目の都合ではなくこの理由からです。

全体の流れ

3 つをつないで、失敗の道も用意すると次の形になります。

const status = document.querySelector("#status"); async function loadWorks() { status.textContent = "読み込み中"; try { const response = await fetch("https://yumesaku.tech/course-api/works.json?delay=800"); if (!response.ok) { throw new Error("作品を読み込めませんでした (" + response.status + ")"); } const data = await response.json(); const selected = selectWorks(data.works); renderWorks(selected); status.textContent = selected.length + " 件の作品を読み込みました"; } catch (error) { renderWorks([]); status.textContent = error.message; } } loadWorks();

status の 1 行が、画面の状態をそのまま表しています。読み込み中なのか、何件出ているのか、失敗したのか。利用者が知りたいことは、この 3 つだけです。

件数は data.count ではなく、実際に描いた selected.length から作ります。絞り込んだあとなので、2 つの数はずれます。画面に出す数字は、画面に出ているものから数えてください。

もう一度読み込めるようにする

失敗したまま何もできない画面は不親切です。やり直すボタンを置いて、押されたら同じ関数をもう一度呼びます。ここまで loadWorks を 1 本の関数にしておいたので、足すのは 3 行だけです。

const reloadButton = document.querySelector("#reload"); reloadButton.addEventListener("click", () => { loadWorks(); });

loadWorks の中で毎回「読み込み中」から始まり、renderWorks が一覧を消してから描き直すので、2 回押してもカードが二重にならないことを確かめてください。ここが二重になるなら、消す処理がどこかで抜けています。

押している間にボタンを押せなくする、という配慮もできます。第8章で覚えた考えかたと同じです。

async function loadWorks() { reloadButton.disabled = true; status.textContent = "読み込み中"; try { // ... 通信と描画 ... } catch (error) { // ... 失敗の表示 ... } finally { reloadButton.disabled = false; } }

finally に置くのが要点です。成功しても失敗しても、ボタンは必ず押せる状態に戻さなければなりません。第6章で finally を「どちらの道でも通る場所」と説明したのは、こういう後始末のためでした。

動いていることをどう確かめるか

書き終えたら、URL の後ろを変えて 3 通り試してください。練習用の口は、そのために挙動を変えられるようにしてあります。

付ける指定確かめられること
?delay=800読み込み中が見えて、そのあと消えるか
?status=500失敗の文が出て、カードが空になるか
?empty=10 件のときに画面が壊れないか

?empty=1 は作品が 0 件で返ってきます。このとき renderWorks([]) が呼ばれ、一覧は空になります。空でも壊れないかは、実際に試さないと分かりません。「0 件です」と出すところまで作れると、より丁寧な画面になります。

通信を使う画面は、うまくいったときだけ試しても完成しません。遅いとき、失敗したとき、中身が空のとき。この 3 つを自分で起こして確かめるところまでが作業です。

よくある間違い

手で書いた配列を消し忘れる

// 直したい書きかた。取ってきたデータを描いたあと、古い配列で上書きしている const works = [{ title: "プロフィールサイト", views: 120 }]; loadWorks(); renderWorks(works); // 出どころをひとつにする。手書きの配列はもう要らない loadWorks();

選ぶ処理を描画の中に混ぜる

// 直したい書きかた。renderWorks が絞り込みまで知ってしまう function renderWorks(works) { works.forEach((work) => { if (!work.published) return; list.append(createCard(work)); }); } // 選んでから渡す。描く側は渡されたものを全部描く renderWorks(selectWorks(data.works));

失敗したときにカードを消し忘れる

// 直したい書きかた。前のカードが残ったままエラー文が出る catch (error) { status.textContent = error.message; } // 画面を失敗したなりに整える catch (error) { renderWorks([]); status.textContent = error.message; }

課題

  1. fetch より先に #status へ「読み込み中」を入れる
  2. selectWorks は published が true のものだけを残し、views の多い順に並べて返す
  3. createWorkCard は views が 100 以上の作品に featured クラスを付ける
  4. #status に、実際に描いた件数を出す
  5. response.ok が false なら throw し、catch で一覧を空にしてエラー文を出す

ヒント

index.html
script.js
プレビュー

できているか

  • fetch を送る前に読み込み中を出す
  • 公開されている作品のカードが並ぶ
  • 取ってきた作品名が画面に出る
  • 絞り込んだあとの件数を出す
  • 表示回数の多い作品が先頭に来て印が付く