初心者のプログラミング日記

プログラミング初心者の日記

プログラミングに関することを書いていきます。

Next.jsでページネーションをする

最近Next.jsを使い始めたので、簡単なページネーションをしてみました。
今回はSSGをしています。
とりあえず、チュートリアルの通りにVercelでデプロイしてみました。
完成した物
https://next-pagination.vercel.app/



codesanboxにコードをあげようかと思ったんですが、上手く環境構築ができず、今回はGitHubの方にコードを挙げておきますので参考にどうぞ。
https://github.com/matsudasan/next-pagination

参考動画
https://youtu.be/IYCa1F-OWmk

APIはここを利用しています
https://jsonplaceholder.typicode.com/


index.js

import Head from 'next/head'
import fetch from "node-fetch";
import { useState } from 'react';
import Pagination from '../components/Pagination';
import Post from '../components/Post';

export default function Home({ posts }) {

  //現在のページ
  const [currentPage, setCurrentPage] = useState(1);

  //ページごとの投稿数
  const [postsPerPage] = useState(10);

  // Get current posts
  //ページの最後の投稿 1ページ目の場合 1×10
  const indexOfLastPost = currentPage * postsPerPage;

  //ページ最初の投稿 1ページ目の場合 10-10
  const indexOfFirstPost = indexOfLastPost - postsPerPage;

  //ページの投稿内容
  const currentPosts = posts.slice(indexOfFirstPost, indexOfLastPost);

  // Change page
  const paginate = pageNumber => setCurrentPage(pageNumber);

  return (
    <div className="container">
      <Head>
        <title>投稿一覧</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>
        <h1>投稿一覧:{posts.length}件</h1>
        <h2>{currentPage}ページ目({indexOfFirstPost+1}~{indexOfLastPost}件表示)</h2>
        <Post posts={currentPosts} />
      </main>
      <Pagination
        postsPerPage={postsPerPage}
        totalPosts={posts.length}
        paginate={paginate}
        currentPage={currentPage}
      />

      <style style jsx global>{`
      ul{
        list-style:none;
        padding:0;
      }
      `}</style>
    </div>
  )
}

//SSG
export async function getStaticProps() {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");
  const posts = await res.json();
  return {
    props: { posts }
  }
}

簡単に詳しい動きはコメントにまとめてあります。
getStaticPropsを使うことでSSGができます。

[id].js

import Head from 'next/head'
import fetch from "node-fetch";

export default function Post({ post }) {
    return (
        <div>
            <Head>
                <title>{post.title}</title>
            </Head>
            <main>
                <h1>内容</h1>
                <p>{post.body}</p>
            </main>
        </div>
    )
}

//SSGするルート
export async function getStaticPaths() {
    const res = await fetch("https://jsonplaceholder.typicode.com/posts");
    const posts = await res.json();
    const paths = posts.map(post => `/posts/${post.id}`);
    return { paths, fallback: false };
}

//SSGしたルートに入る内容
export async function getStaticProps({ params }) {
    console.log(params)
    const id = params.id
    const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
    const post = await res.json();
    return {
        props: { post }
    }
}

fallbackはfalseの場合、ビルドしていないパスにアクセスした場合404のnot pageになり
trueの場合404にはならず、パスを生成すると書いてありました。
trueの時の挙動はあまりわかっていません。

公式ページ
https://nextjs.org/docs/basic-features/data-fetching

Post.js

import Link from 'next/link'

export default function Posts({ posts }) {

    return (
        <>
            <ul className='list-group'>
                {posts.map(post => (
                    <li key={post.id} className='list-group-item'>
                        <Link href="/posts/[id]" as={`/posts/${post.id}`}><a>{post.title}</a></Link>
                    </li>
                ))}
            </ul>
        <style jsx>{`
        .list-group-item{
            padding: 20px 10px;
            border-bottom: 1px solid #d7d2cd;
            display: flex;
            align-items: center;
        }
        `}</style>
        </>
    );
};

渡された投稿を表示しているだけです

Pagination.js

import Router from 'next/router'

export default function Pagination({ postsPerPage, totalPosts, paginate, currentPage }) {
 
    const pageNumbers = [];

    //ページ数を決める
    for (let i = 1; i <= Math.ceil(totalPosts / postsPerPage); i++) {
        pageNumbers.push(i);
    }

    function Paginate(number) {
        //クエリパラメータ-
        Router.push(`/?page=${number}`)
        paginate(number)
    }
    return (
        <>
            <nav>
                <ul className='pagination'>
                    {pageNumbers.map(number => {
                        let active = number === currentPage ? "active" :"";
                        return (
                            <li key={number} className={`page-item ${active}`} onClick={() => Paginate(number)}>
                                <a className='page-link' >
                                    {number}
                                </a>
                            </li>
                        )
                    })}
                </ul>
            </nav>
            <style jsx>{`
            .pagination{
                display:flex;
                height:30px;
            }
            .page-item{
                border: 1px solid #d7d2cd;
                width:30px;
                cursor:pointer;
                text-align:center;

            }
            .page-link{
                display:block;
                line-height:30px;
            }
            .active{
                background-color:#666666;
            }
            `}</style>
        </>
    );
};

ここはほぼ動画のままです。

最後にSSGされているかの確認をします

npm run build

このコマンドで確認できます。

f:id:nasubiFX:20200730175422p:plain

●がSSGされているパスです。
しっかりSSGされていることが分かります。

今回は以上です