【Next.js】getServerSidePropsでJSON一覧から個別情報を取得するメモ
概要
Next.jsでJSONファイルなど情報を取ってくることが多いと思うが、Next.jsの場合、getServerSideProps
を使って取ってくる事ができるので防備ログとして残す。
なお、getStaticProps
とりあえず今回はなしで。
最終目的
最終的な目的として、以下のことを最終目的とする。
- jsonファイルからデータを取得して一覧表示する
- 一覧をクリックしたら個別ページにその情報を表示する
getServerSidePropsとは?
Next.js では、[param] のようにして角括弧([])を使って動的なルーティングを作成できる。
これをダイナミックルーティングというがgetServerSideProps
/getStaticProps
を機能使用して実現できる。
getServerSideProps
はNext.jsで使うことのできる関数で、関サーバーサイドで実行される。
ちなみgetStaticProps
との違いは
- getStaticPropsはビルドのタイミングで実行される
- getServerSidePropsはリクエスト毎に実行される
その為、リクエスト毎にレスポンスを返すのでgetStaticProps
と違って、読み込みに時間がかかることがある。またNext.jsでプロジェクトを作成すると「pages」ディレクトリが作成されるのですが、getServerSideProps
もgetStaticProps
も「pages」ディレクトリ以外は使えない。
ダイナミックルーティングとは?
Next.js では、pages/posts/[id].jsのようなファイル名でページを作成すると、1つのファイルで、以下のようなURLでアクセスができる。
/posts/01
/posts/02
/posts/03
これを ダイナミックルーティング機能というらしい。
実際の流れ
では、実際の流れをメモしていく。今回はJSONファイルとしてjsonplaceholderというダミーのjsonファイルを用意してくれているサービスを利用して、今回は記事を想定して以下のURLから記事データを取得する。
一覧を読み込むページはpage/index.jsで表示します。
JSONを読み込んで一覧ページを作成
実際に読み込み処理を書きます。以下のjsonファイルを読み込みます。
https://jsonplaceholder.typicode.com/posts
//1つの記事のスキーマは以下のようになってる
{
"userId": 1,
"id": 3,
"title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
"body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi a"
},
JSONファイルを読み込む
jsonファイルを読み込む処理は以下。
export default function Home() {
return (
<div>
<h1>一覧を表示させる</h1>
</div>
)
}
export async function getServerSideProps() {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts`)
const posts = await res.json()
return { props: { posts } }
}
getServerSideProps
はHome()
の外でexport
を利用して使用する。
getServerSideProps
内でfetch()
で取得したデータからJSONを取得しDOM操作に利用する必要があるので、getServerSideProps
にasync
を付けて、fatch()
とres.json()
にはawait
をつけて非同期処理としています。
その結果を返してあげる必要があるので、return{}
でpropsプロパティにオブジェクトでデータを渡す。
また上記のコードはNext.jsの公式サイトに載っていて、割と外部APIからデータを取ってくるときのベースとなる書き方のようである。
JSONファイルを表示する
今度は取得したJSONファイルを表示する必要がある。
export default function Home(data) {
return (
<div>
<h1>一覧を表示させる</h1>
<ul>
{data.posts.map((item)=>{
return(
<li key={item.id}>
<Link href={`/posts/${item.id}`}>
<a>{item.title}</a>
</Link>
</li>
)
})}
</div>
)
}
JSONファイルはgetServerSideProps
のprops
に渡したデータは、Home(data)
に引数から受け取ることができる。プロパティ名をdata
としているがなんでもいい。
その後、map関数で各データを取り出して表示してます。
ちなみに、data.posts
の「posts」という名前は、getServerSideProps
のprops: { posts }
で渡している変数名。
またルーティング用のコンポーネントであるLINKには、JSONファイルには各記事の「id」があったのでhref
に設定しておきます。hrefのパスの階層は後に作成する個別ファイル([id].js
)と同じ階層にする。
この「id」番号は記事詳細のファイルで再度fetch()
で読み込む際に必要となります。
個別ページを作成・表示
一覧ページを作成したら今度は個別ページを作成します。
個別ページでも同様に、getServerSideProps
を利用します。
個別ファイルを作る
今回はpages/posts/[id].js
としたいので、pagesディレクトリにpostsフォルダを作成してそこに[id].js
を作成。
[id].js
にも同様に、getServerSideProps
の処理を記載しますが、ほとんど前回と同じ。
違いは、getServerSideProps
の引数にcontext
が設定されてる事。
export const getServerSideProps=async (context)=>{
console.log(context)
const id=context.query.id
const res=await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
const post=await res.json()
return{
props:{post}
}
}
console.log(context)
で確認するとわかるが、このcontext
にいろんな情報が格納されている。
今度は記事一覧ではなく、各記事のデータが欲しいので、JSONファイルから各記事の「id」を指定したURLからfetch()
による非同期処理で取得する必要がある。
各記事の「id」番号がcontext
のquery
(クエリ文字列を表すオブジェクト)の中にid番号が入ってる。
query: { id: '6' },
この「id」は一覧ページでhref
属性に設定した記事の「id」番号である。
ちなみに今回[id].js
とファイル名を指定しているが、[post].js
だった場合は、contextのqueryは以下になる。
query: { post: '6' },
あとは同様にprops
に記事のデータを指定してreturn
してあげる。
contextからidを取得する際の注意点
context
からidを取得する場合は、query
(クエリパラメータ)から取得していましたが、
context
の中身を見るとparams
の中にもidがあることが分かります。
query: { id: '4' },
resolvedUrl: '/posts/4',
params: { id: '4' },
その為、以下のようにどちらからでも取得できる。
export const getServerSideProps=async (context)=>{
const id=context.query.id
}
getStaticProps
の場合は、query'(クエリパラメータ)は存在せず、
params`になるので以下のようになる。
export const getStaticProps=async (context)=>{
const id=context.params.id
}
記事の内容を表示する
こちらは一覧ページと同じである。
export default function post(param) {
return (
<>
<h1>POST(投稿IDは){param.post.id}</h1>
<h2>タイトル:{param.post.title}</h2>
<div>
<p>本文</p>
{param.post.body}
</div>
</>
)
}
param
という引数を指定してあげて、その中に記事のタイトル(param.post.title
)や本文(param.post.body
)の内容が格納されてるので、出力すれば表示される。
一覧同様でparam.post
のpost
は、getServerSideProps
で指定したprops:{post}
と同じ変数名でアクセスできる。
以上で記事の内容が表示される。
分割代入による引数の設定
ちょっとした余談。
一覧ページや記事の個別ページでも引数を受け取る際に、分割代入を利用すれば省略して記載できる。
//通常のパターン
export default function post(param) {
return (
<>
<h2>タイトル:{param.post.title}</h2>
</>
)
}
//分割代入で引数を指定
export default function post({post}) {
return (
<>
<h2>タイトル:{post.title}</h2>
</>
)
}
参考サイト
Discussion