🐈

【Next.js】getServerSidePropsでJSON一覧から個別情報を取得するメモ

2022/05/22に公開

概要

Next.jsでJSONファイルなど情報を取ってくることが多いと思うが、Next.jsの場合、getServerSidePropsを使って取ってくる事ができるので防備ログとして残す。
なお、getStaticPropsとりあえず今回はなしで。

最終目的

最終的な目的として、以下のことを最終目的とする。

  • jsonファイルからデータを取得して一覧表示する
  • 一覧をクリックしたら個別ページにその情報を表示する

getServerSidePropsとは?

Next.js では、[param] のようにして角括弧([])を使って動的なルーティングを作成できる。
これをダイナミックルーティングというがgetServerSidePropsgetStaticPropsを機能使用して実現できる。

getServerSidePropsはNext.jsで使うことのできる関数で、関サーバーサイドで実行される。
ちなみgetStaticPropsとの違いは

  • getStaticPropsはビルドのタイミングで実行される
  • getServerSidePropsはリクエスト毎に実行される

その為、リクエスト毎にレスポンスを返すのでgetStaticPropsと違って、読み込みに時間がかかることがある。またNext.jsでプロジェクトを作成すると「pages」ディレクトリが作成されるのですが、getServerSidePropsgetStaticPropsも「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ファイルを読み込む処理は以下。

pages/index.js
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 } }
}

getServerSidePropsHome()の外でexportを利用して使用する。
getServerSideProps 内でfetch()で取得したデータからJSONを取得しDOM操作に利用する必要があるので、getServerSidePropsasyncを付けて、fatch()res.json()にはawaitをつけて非同期処理としています。

その結果を返してあげる必要があるので、return{}propsプロパティにオブジェクトでデータを渡す。

また上記のコードはNext.jsの公式サイトに載っていて、割と外部APIからデータを取ってくるときのベースとなる書き方のようである。

JSONファイルを表示する

今度は取得したJSONファイルを表示する必要がある。

pages/index.js
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ファイルはgetServerSidePropspropsに渡したデータは、Home(data)に引数から受け取ることができる。プロパティ名をdataとしているがなんでもいい。
その後、map関数で各データを取り出して表示してます。
ちなみに、data.postsの「posts」という名前は、getServerSidePropsprops: { posts }で渡している変数名。

またルーティング用のコンポーネントであるLINKには、JSONファイルには各記事の「id」があったのでhrefに設定しておきます。hrefのパスの階層は後に作成する個別ファイル([id].js)と同じ階層にする。

この「id」番号は記事詳細のファイルで再度fetch()で読み込む際に必要となります。

個別ページを作成・表示

一覧ページを作成したら今度は個別ページを作成します。
個別ページでも同様に、getServerSideProps を利用します。

個別ファイルを作る

今回はpages/posts/[id].jsとしたいので、pagesディレクトリにpostsフォルダを作成してそこに[id].jsを作成。

[id].jsにも同様に、getServerSidePropsの処理を記載しますが、ほとんど前回と同じ。
違いは、getServerSidePropsの引数にcontextが設定されてる事。

pages/[id].js
 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」番号がcontextquery(クエリ文字列を表すオブジェクト)の中に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' },

その為、以下のようにどちらからでも取得できる。

pages/[id].js
export const getServerSideProps=async (context)=>{
  const id=context.query.id
}

getStaticPropsの場合は、query'(クエリパラメータ)は存在せず、params`になるので以下のようになる。

pages/[id].js
export const getStaticProps=async (context)=>{
  const id=context.params.id
}

記事の内容を表示する

こちらは一覧ページと同じである。

pages/posts/[id].js
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.postpostは、getServerSidePropsで指定したprops:{post}と同じ変数名でアクセスできる。

以上で記事の内容が表示される。

分割代入による引数の設定

ちょっとした余談。
一覧ページや記事の個別ページでも引数を受け取る際に、分割代入を利用すれば省略して記載できる。

pages/posts/[id].js
//通常のパターン
export default function post(param) {
    return (
       <>
        <h2>タイトル:{param.post.title}</h2>
        </>
    )
  }
 
//分割代入で引数を指定
export default function post({post}) {
    return (
       <>
        <h2>タイトル:{post.title}</h2>
       </>
    )
  }

参考サイト

https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props
https://reffect.co.jp/react/next-js#getServerSideProps
https://www.sukerou.com/2022/02/nextjs-getserversideprops.html

Discussion