初心者による初心者のためのGatsbyJS覚書10（ブログの記事一覧ページを作成する）

#この記事について
この記事はGatsbyJS初心者の新卒２年目が作成しています。
参考資料として書籍を使用していますが、筆者がビギナークラスのため読んでいて「？」となる部分や間違っている箇所もあるかと思います。
参考までに、そして間違えている箇所がありましたらご連絡いただけると嬉しいです。

##Chapter10：ブログの記事ページを自動生成する　備忘録メモ

1.記事一覧ページを作成する
2.ページごとの表示数を設定し、記事一覧ページを生成する
3.ページネーションを追加する
4.ナビゲーションメニューに記事一覧のリンクを設定する
※他のチャプターで記載しているメタデータの設定やFont Awesomeの使用については割愛します。

###記事一覧ページを作成する
`pages`の配下に`blog.js`を作成し、ベースのHTMLをJSXに変換してページを作成していきます。
他のチャプターで紹介している流れでGraphQLから各記事のデータを取得し、タイトルやアイキャッチ画像、そしてリンクなどを
組み込んで行きます。

```jsx
const BlogPage = ({ data, location }) => (
  <Layout>
  <Seo 
    pagetitle="ブログ"
    pagedesc="ESSENTIALのブログです"
    pagepath={location.pathname}/>
    <section className="content bloglist">
      <div className="container">
        <h1 className="bar">RECENT POSTS</h1>
        <div className="posts">
        {data.allContentfulBlogPost.edges.map(({node}) => (
          <article className="post" key={node.id}>
            <Link to={`/blog/post/${node.slug}`}>
              <figure>
              <GatsbyImage
                image={node.eyecatch.gatsbyImageData}
                alt={node.eyecatch.description}
                style={{ height: "100%" }}
              />
              </figure>
              <h3>{node.title}</h3>
            </Link>
          </article>
          ))}
        </div>
      </div>
    </section>
  </Layout>
)

export const query = graphql`
query{
  allContentfulBlogPost(
    sort: {order: DESC, fields: publishDate}
  ) {
    edges {
      node {
        title
        id
        slug
        eyecatch {
          gatsbyImageData(width: 500, layout: CONSTRAINED)
          description
        }
      }
    }
  }
}
`
export default BlogPage
```

###ページごとの表示数を設定し、記事一覧ページを生成する
skipとlimitを使用して1ページにつき6件の記事一覧を作っていきます。

先程作った`blog.js`記事数によって何ページか自動生成される必要があるため、
　`template/`配下に`blog-template.js`と名前を変えて配置し直します。

`gatsby-node.js`に下記を付け加えてページを生成します。

```js
const path = require("path")

exports.createPages = async({ graphql, actions, reporter }) => {
  const { createPage } = actions

  const blogresult = await graphql(`
  ・・・・

  if(blogresult.errors){
  ・・・・
  }

  blogresult.data.allContentfulBlogPost.edges.forEach(({ node, next, previous }) => {
  ・・・・
  })

　　　　//下記を追加
  const blogPostPerPage = 6;　//1ページの表示件数
  const blogPosts = blogresult.data.allContentfulBlogPost.edges.length　//記事の総数
  const blogPages = Math.ceil(blogPosts / blogPostPerPage )//記事一覧ページの総数

  Array.from({length: blogPages}).forEach((_, i) => {
    createPage({
      path: i === 0 ? `/blog/` : `/blog/${i + 1}/`,　
      component: path.resolve("./src/templates/blog-template.js"), 
　　　　　　　　　　　　context: {
        skip: blogPostPerPage * i,
        limit: blogPostPerPage,
      },
    })
  })
}
```
記事一覧ページの表示件数を指定し、全記事数から表示件数を割って配列を展開させます。
パスは配列を展開するときに使うiを利用してiが０のときは/blog/０でないときは/blog/numといった形になるように指定しています。
また、contextでは全体の記事からそのページで表示する記事を選別するときに
スキップしたい記事をblogPostPerPageにiをかけた数、また表示する件数をblogPostPerPage指定しています。

`blog-template.js`でもクエリにこのような指定を加えます。

```jsx
//contextで取得するskipとlimitの値を使用して記事を取得する
export const query = graphql`
query($skip: Int!, $limit: Int!){
  allContentfulBlogPost(
    sort: {order: DESC, fields: publishDate}
    limit: $limit
    skip: $skip
  ) {
    edges {
      node {
        title
        id
        slug
        eyecatch {
          gatsbyImageData(width: 500, layout: CONSTRAINED)
          description
        }
      }
    }
  }
}
`
```

###ページネーションを追加する

`gatsby-node.js`にページネーションを機能させるのに必要なデータを送るためのcontextを追加します。

```js
    Array.from({length: catPages}).forEach((_, i) => {
      createPage({
        path: i === 0 
        ? `/cat/${node.categorySlug}/` 
        : `/cat/${node.categorySlug}/${i + 1}/`,
        component: path.resolve("./src/templates/blog-template.js"),
        context: {
          catid: node.id,
          catname: node.category,
          catslug: node.categorySlug,
          skip: catPostPerPage * 1,
          limit: catPostPerPage,
          currentPage: i + 1, //【追加】現在のページ番号 
          isFirst: i + 1 === 1, //【追加】最初のページ
          isLast: i + 1 === catPages, //【追加】最後のページ 
        },
      })
    })
```

`blog-template.js`にページネーションのJSXを追加します。

```jsx
<ul class="pagenation">
  {!pageContext.isFirst && (
    <li class="prev">
        <Link to={
          pageContext.currentPage === 2
          ? `/blog/`
          : `/blog/${pageContext.currentPage - 1}/`
        }
        rel="prev"
        >
            <FontAwesomeIcon icon={faChevronLeft}/>
            <span>前のページ</span>
        </Link>
    </li>
    )}
    {!pageContext.isLast && (
    <li class="next">
      <Link to={`/blog/${pageContext.currentPage + 1}/`} rel="next">
            <span>次のページ</span>
            <FontAwesomeIcon icon={faChevronRight}/>
      </Link>
    </li>
    )}
</ul>
```
 `!`でcontextで送られてくる値の有無を判別し、
isFirstやisLastがないとき（最初のページや最後のページ）はリンクが表示されないようにしています。
また、三項演算子を使用してページのパスを指定しています。
前のページのリンクについてはcurrentPageが２の場合は前のページパスが/blog/となるのでそれを指定し、
それ以外は現在のページ番号から１引いた/blog/num/へ遷移するようになっています。
逆に次のページへのリンクは現在のページ番号から１足した/blog/num/へ遷移するようになっています。

![スクリーンショット 2021-09-26 23.42.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281399/bff9ce17-e715-d161-67b7-6b0fa41a4572.png)

###ナビゲーションメニューに記事一覧のリンクを設定する

`header.js`にブログ一覧へ遷移できるリンクを追加します。

```jsx
<nav className="nav">
  <ul>
    <li><Link to={`/`}>TOP</Link></li>
    <li><Link to={`/about/`}>ABOUT</Link></li>
    <li><Link to={`/blog/`}>BLOG</Link></li> //追加
  </ul>
```