LoginSignup
0
0

More than 1 year has passed since last update.

Next.js の SG, ISR (with Apollo) の仕組みを作りながら学んでみた(ユーザーの詳細ページのコンポーネント作成)4/5

Last updated at Posted at 2021-11-21

はじめに

Next.js の SG + ISR (with Apollo) の仕組みを作りながら学んでみたシリーズ

このシリーズでは、Hasuraで用意した仮想のユーザーデータを外部APIとして取得しながら、

ユーザーの一覧ページと詳細ページを作りながら、SGと ISRを学ぶ記事です。

今回は、ユーザーの詳細ページのコンポーネント作成の実装について書きます。

目次

SGという用語に関して

SSGと言ったり、SGと言ったりするかもしれませんが、SSGはかつての呼び方なのでSG(Static Generate)に統一して書こうと思います。

仕組みは同じものだと思ってください。

最終的なゴール

以下のような構成のアプリを作ることです。

スクリーンショット 2021-10-18 17.27.41.png

目的

  • 仕事で使っている技術のキャッチアップと復習
  • 使う可能性がある技術の理解度向上

詳細ページに必要なファイル作成

pages/
└── users/
    └── [id].tsx

なんでこの位置に?なんでブラケット付き?と言う疑問はこちらで👇

必要なモジュールのimport

import { VFC } from 'react'
import Link from 'next/link' // 一覧ページへリンクするので
import { GetStaticProps, GetStaticPaths } from 'next' // 各関数の型
import { ChevronDoubleLeftIcon } from '@heroicons/react/solid'
import { initializeApollo } from '../../lib/apolloClient' // Hasuraにデータ取得するためのGraphQlクライアントメソッド
import { GET_USERSIDS, GET_USERBY_ID } from '../../queries/queries' // Hasuraにデータ取得するためのクエリ
import { GetUsersByIdQuery, GetUserIdsQuery, Users } from '../../types/generated/graphql'   // クエリを書いたら自動生成される型情報
import { Layout } from '../../components/Layout'

ChevronDoubleLeftIcon<<的なアイコンです。

Paul.png

interface作成

前回実装した関数getStaticPropsからreturnされる users

ここね👇

return {
  props: { users: data.users },
  revalidate: 1,
}

このusersは今回実装するコンポーネントのpropsに渡ってきます。

そのpropsの型をinterfaceで定義します。

interface Props {
  user: {
    __typename?: 'users'
  } & Pick<Users, 'id' | 'name'>
}

中身がオブジェクトになっていて、typesがusersidnameが定義されます。

Usersをホバーすると全体感把握できますが、その中から必要なものをPick UP してidnameって感じです。

スクリーンショット 2021-11-21 8.59.33.png

実際に以下の記事(Hasura)で作成してきたユーザーデータを

一覧ページで関数getStaticProps経由で取得した結果を1つ1つ詳細ページで表現した感じです。

console.log(users)

これで見てみると

[
    {
        "__typename": "users",
        "id": "5f172cd2-221c-4ea5-8b83-6420b18860ab",
        "name": "lilly"
    },
    {
        "__typename": "users",
        "id": "0458dd7d-70a5-4087-88c7-d8c3ed51a505",
        "name": "bob"
    },
    {
        "__typename": "users",
        "id": "f6a27de5-2f26-4e14-8435-bc463aabe791",
        "name": "Paul"
    }
]

interface通りですね。

コンポーネント作成

const UserDetail: VFC<Props> = ({ user }) => {
  if (!user) {
    return <Layout title="loading">Loading...</Layout>
  }
  return (
    <Layout title={user.name}>
      <p className="text-xl font-bold">User detail</p>
      <p className="m-4">{`ID : ${user.id}`}</p>
      <p className="mb-4 text-xl font-bold">{user.name}</p>
      <Link href="/hasura-ssg">
        <div className="flex cursor-pointer mt-12">
          <ChevronDoubleLeftIcon
            data-testid="auth-to-main"
            className="h-5 w-5 mr-3 text-blue-500"
          />
          <span data-testid="back-to-main">Back to main-ssg-page</span>
        </div>
      </Link>
    </Layout>
  )
}
export default UserDetail

以下の条件でコンポーネントを出し分けています。

userが存在しない場合

if (!user) {
  return <Layout title="loading">Loading...</Layout>
}

userが存在する場合

return (
  <Layout title={user.name}>
    <p className="text-xl font-bold">User detail</p>
    <p className="m-4">{`ID : ${user.id}`}</p>
    <p className="mb-4 text-xl font-bold">{user.name}</p>
    <Link href="/hasura-sg">
      <div className="flex cursor-pointer mt-12">
        <ChevronDoubleLeftIcon
          data-testid="auth-to-main"
          className="h-5 w-5 mr-3 text-blue-500"
        />
        <span data-testid="back-to-main">Back to main-sg-page</span>
      </div>
    </Link>
  </Layout>
)

新たにSGするファイルを作る際やgetStaticPropsとgetStaticPathsを作った結果をデバッグしたい時は yarn build してからブラウザで確認してください

$ yarn build
$ yarn start

作成したファイル

長いので折り畳みました。
pages/users/[id].tsx
import { VFC } from 'react'
import Link from 'next/link'
import { GetStaticProps, GetStaticPaths } from 'next'
import { ChevronDoubleLeftIcon } from '@heroicons/react/solid'
import { initializeApollo } from '../../lib/apolloClient'
import { GET_USERSIDS, GET_USERBY_ID } from '../../queries/queries'
import {
  GetUsersByIdQuery,
  GetUserIdsQuery,
  Users,
} from '../../types/generated/graphql'
import { Layout } from '../../components/Layout'

interface Props {
  user: {
    __typename?: 'users'
  } & Pick<Users, 'id' | 'name'>
}

const UserDetail: VFC<Props> = ({ user }) => {
  if (!user) {
    return <Layout title="loading">Loading...</Layout>
  }
  return (
    <Layout title={user.name}>
      <p className="text-xl font-bold">User detail</p>
      <p className="m-4">{`ID : ${user.id}`}</p>
      <p className="mb-4 text-xl font-bold">{user.name}</p>
      <Link href="/hasura-sg">
        <div className="flex cursor-pointer mt-12">
          <ChevronDoubleLeftIcon
            data-testid="auth-to-main"
            className="h-5 w-5 mr-3 text-blue-500"
          />
          <span data-testid="back-to-main">Back to main-sg-page</span>
        </div>
      </Link>
    </Layout>
  )
}
export default UserDetail

export const getStaticPaths: GetStaticPaths = async () => {
  const apolloClient = initializeApollo()
  const { data } = await apolloClient.query<GetUserIdsQuery>({
    query: GET_USERSIDS,
  })
  const paths = data.users.map((user) => ({
    params: {
      id: user.id,
    },
  }))
  return {
    paths,
    fallback: true,
  }
}
export const getStaticProps: GetStaticProps = async ({ params }) => {
  const apolloClient = initializeApollo()
  const { data } = await apolloClient.query<GetUsersByIdQuery>({
    query: GET_USERBY_ID,
    variables: { id: params.id },
  })
  return {
    props: {
      user: data.users_by_pk,
    },
    revalidate: 1,
  }
}

まとめ

今回は、ユーザーの詳細ページのコンポーネント作成の実装について書きました。

次回

アウトプット100本ノック実施中

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0