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

## はじめに

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

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

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

https://qiita.com/ryosuketter/items/ab7d4c8bfb19185f7b1e

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

## 目次

- 実装
    - 一覧ページ
        - [getStaticProps](https://qiita.com/ryosuketter/items/442db08856093e6cfeea)
        - [必要なモジュールのimport](https://qiita.com/ryosuketter/items/8c4820c06a441c717180) 👈 今回はコレ
        - [interface作成](https://qiita.com/ryosuketter/items/8c4820c06a441c717180) 👈 今回はコレ
        - [コンポーネント作成](https://qiita.com/ryosuketter/items/8c4820c06a441c717180) 👈 今回はコレ
    - 詳細ページ
        - [getStaticProps と getStaticPaths](https://qiita.com/ryosuketter/items/799d3330ab7cda37145c)
        - [必要なモジュールのimport](https://qiita.com/ryosuketter/items/cb2507559c6c71bf9f17)
        - [interface作成](https://qiita.com/ryosuketter/items/cb2507559c6c71bf9f17)
        - [コンポーネント作成](https://qiita.com/ryosuketter/items/cb2507559c6c71bf9f17)
- 検証
    - [作ったものをブラウザで見ながらSGとISRの仕組みをざっくり理解する](https://qiita.com/ryosuketter/items/af45c292249ea7a88d9d)


## SGという用語に関して

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

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

## 最終的なゴール

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

![スクリーンショット 2021-10-18 17.27.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14191/e8a9a2e1-2723-4fec-77ef-be3b9eefdd81.png)

### 目的

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

## 必要なモジュールのimport

```tsx
import { VFC } from 'react'
import Link from 'next/link' // 詳細ページへリンクするので
import { GetStaticProps } from 'next' // 関数getStaticPropsの型
import { initializeApollo } from '../lib/apolloClient' // Hasuraにデータ取得するためのGraphQlクライアントメソッド
import { GET_USERS } from '../queries/queries' // Hasuraにデータ取得するためのクエリ
import { GetUsersQuery, Users } from '../types/generated/graphql'  // クエリを書いたら自動生成される型情報
import { Layout } from '../components/Layout'
```

## interface作成

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

ここね👇

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

https://qiita.com/ryosuketter/items/442db08856093e6cfeea

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

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

```tsx
interface Props {
  users: ({
    __typename?: 'users'
  } & Pick<Users, 'id' | 'name'>)[]
}
```

配列で定義されています。その中身はオブジェクトになっていて、typesが`users`、`id``name`が定義されます。

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

![スクリーンショット 2021-11-21 8.59.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14191/08b7ba55-1def-d498-ae36-732a21363d91.png)

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

https://qiita.com/ryosuketter/items/ab7d4c8bfb19185f7b1e

関数`getStaticProps`経由で取得した結果を

```tsx
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通りですね。

## コンポーネント作成

```tsx
const HasuraSG: VFC<Props> = ({ users }) => {
  console.log(users)
  return (
    <Layout title="Hasura SG">
      <p className="mb-3 font-bold">SG+ISR</p>
      {users?.map((user) => {
        return (
          <Link key={user.id} href={`/users/${user.id}`}>
            <a className="my-1 cursor-pointer" data-testid={`link-${user.id}`}>
              {user.name}
            </a>
          </Link>
        )
      })}
    </Layout>
  )
}
export default HasuraSG
```

`users`が存在すれば、ユーザーの数分だけ詳細ページへのリンクを作成しています。

ブラウザ確認するとSGで取得されたページが表示されます。

ためしにブラウザのJSを無効化してもリロードしても表示されます。

なぜなら、前回説明した`pre-rendering`（意味を先に言うと、事前にHTMLを生成すること）が、適切に機能しているからです。

https://qiita.com/ryosuketter/items/442db08856093e6cfeea

Reactだけで作った場合、こうはなりません。

## 作成したファイル

<details><summary>長いので折り畳みました。</summary><div>

```tsx
import { VFC } from 'react'
import Link from 'next/link'
import { GetStaticProps } from 'next'
import { initializeApollo } from '../lib/apolloClient'
import { GET_USERS } from '../queries/queries'
import { GetUsersQuery, Users } from '../types/generated/graphql'
import { Layout } from '../components/Layout'

interface Props {
  users: ({
    __typename?: 'users'
  } & Pick<Users, 'id' | 'name'>)[]
}

const HasuraSG: VFC<Props> = ({ users }) => {
  console.log(users)
  return (
    <Layout title="Hasura SG">
      <p className="mb-3 font-bold">SG+ISR</p>
      {users?.map((user) => {
        return (
          <Link key={user.id} href={`/users/${user.id}`}>
            <a className="my-1 cursor-pointer" data-testid={`link-${user.id}`}>
              {user.name}
            </a>
          </Link>
        )
      })}
    </Layout>
  )
}
export default HasuraSG

export const getStaticProps: GetStaticProps = async () => {
  const apolloClient = initializeApollo()
  const { data } = await apolloClient.query<GetUsersQuery>({
    query: GET_USERS,
  })
  return {
    props: { users: data.users },
    revalidate: 1,
  }
}
```

</div></details>

## まとめ

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

## 次回

https://qiita.com/ryosuketter/items/799d3330ab7cda37145c

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

https://qiita.com/ryosuketter/items/713474dbd009870e4103