サーバーで取得したデータを画面に表示する
それでは、実際にhackernews-appを編集してみましょう。まずは、通信用の便利関数を用意しておきます(リスト7)。
/** Hacker Newsの人気・最新の記事500件の記事IDを取得する */
export async function getTopStories() {
// (1)
return fetch("https://hacker-news.firebaseio.com/v0/topstories.json", {
next: { revalidate: 60 }, // (2) 1分経過するとキャッシュを破棄する
}).then(
(res) => res.json()
);
}
/** 記事の詳細データを取得する */
export async function getItem(id) {
return fetch(https://hacker-news.firebaseio.com/v0/item/${id}.json
).then(
(res) => res.json()
);
}
まず、Pages Routerに慣れた方は、appフォルダ内に便利関数のファイルを置いていることに戸惑われたかと思います。App Routerではこういったファイルも app
フォルダ内に置けるようになりました。page.js
や layout.js
をはじめとした予約されたファイル名には特別な役目が与えられているため、その役割に見合った実装が必要ですが、それらのファイル名を避ければ、フォルダもファイルも自由に配置してOKです。特に、リスト7で配置した app/_utils
フォルダのように、先頭にアンダースコアをつけたフォルダは、ルーティングから完全に除外されるので、UIと関係のないファイルを置く用途に適しています。
関数の内部に目を向けると、サーバーで使用する処理ながら、(1)で fetch
関数を利用しているのが見えます。App Routerで動作しているサーバー側の処理の中では、Web標準とほとんど同じインターフェースの fetch
関数が整備されているので、ブラウザで学んだ知識がそのまま使用できます。ただし、App Routerの fetch
は少しカスタマイズされている点に注意が必要です。同じURLからのGETリクエストは、デフォルトでキャッシュが効いて、2回目以降のアクセスでは1回目の処理結果をそのまま返すようになっています。時間経過で内容が変わりそうなものについては、(2)のように next.revalidate
オプションでキャッシュを破棄する時間を指定すると良いでしょう。
なお、前述しましたが、getTopStories()
で取得できるのは記事のIDの配列だけです。そのため、タイトルなどを取得するためには、各IDを getItem()
に渡して、記事データを入手する必要があります。
それでは、リスト7の関数を使って、人気・最新の記事20件を表示するUIを作成してみましょう。app/top20/page.js
を作成して、リスト8のコードを記述します。CSSファイルはサンプルコードに入っていますので、手元で動かしてみる場合はご利用ください( app/globals.css
も上書きしてください)。
import Link from "next/link"
import "./_style/top20.css"
import { getItem, getTopStories } from "../_utils/hackerNews";
export default async function Top20Page() {
// (1) データをして加工する
// 500件のデータを取得する
const top500Ids = await getTopStories();
// 上位20件のIDだけに絞り込む
const top20Ids = top500Ids.slice(0, 20);
// 上位20件の記事データを取得する
const top20 = await Promise.all(top20Ids.map((id) => getItem(id)));
// 記事データのIDとタイトルだけに絞り込み、
// idとtitleのみのオブジェクトが20件入った配列にする
const top20Summary = top20.map((item) => ({
id: item.id,
title: item.title,
}));
return (
<div>
<header>
<h1>Hacker News Viewer</h1>
</header>
<div id="container">
<div id="sidebar">
<h2>Top 20</h2>
<nav>
<ul>
{top20Summary.map((item) => (
<li key={item.id}>
{/* (2) タイトルをリンクにする */}
<Link href={/top20/${item.id}
}>{item.title}</Link>
</li>
))}
</ul>
</nav>
</div>
<main>
<div>(3) 本文をここに表示する</div>
{/* {children} */}
</main>
</div>
</div>
)
}
(1)では、リスト3の便利関数を使いながら、20件分の記事データの配列を作り出しています。記事データの配列は、リストとして表示し、(2)でリンクにしました。次回、このリンク先のページも作成します。リンクをクリックしたら、(3)の領域に本文を表示する予定ですが、少し特殊な仕組みを使う予定なので、今のところは仮置きのテキストを表示するだけにしておきます。
では、実際に動かしてみましょう。ターミナルで npm run dev
を実行して、http://localhost:3000/top20
にアクセスします(図6)。
記事のリストをメニューとして表示できました。ちゃんと20件のリンクが並んでいますね。リンクをクリックしてみると、404 Not Found
が表示されますが、まだ該当のページを作成していないので、期待通りの挙動です。
まとめ
今回は、Next.js 13.4で安定版になったApp Routerについて、特徴を簡単に解説しつつ、サンプルコードの解説を始めました。React Server ComponentsはReactの経験が長い人であっても慣れるのに時間がかかる機能です。今後の連載を通じて親しめるものになるよう、解説します。
次回はサンプルの続きを題材にして、layout.js
の実践的な使い方を解説していきます。お楽しみに。