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の時の挙動はあまりわかっていません。
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
このコマンドで確認できます。
●がSSGされているパスです。
しっかりSSGされていることが分かります。
今回は以上です