📄

Next.js / MUI (Material UI) でページネーション作成

2021/10/10に公開
1

Next.js で SSR しているサイトのページネーションを MUI(旧 Material UI)で作成する。
MUI には Pagination というコンポーネントがあるが、そのまま使うとカスタマイズしづらいので usePagination という hook を使う。

MUI をインストールする

// npm
npm install @mui/material @emotion/react

// yarn
yarn add @mui/material @emotion/react

Pagination コンポーネントを作成する

usePagination のサンプルコードを参考に Pagination コンポーネントを作成する。

Pagination.tsx
import usePagination from '@mui/material/usePagination'
import { css } from '@emotion/react'

export const Pagination = () => {
  const { items } = usePagination({
    count: 10,
  })

  return (
    <nav>
      <ul css={list}>
        {items.map(({ page, type, selected, ...item }, index) => {
          let children = null

          if (type === 'start-ellipsis' || type === 'end-ellipsis') {
            children = '…'
          } else if (type === 'page') {
            children = (
              <button
                type="button"
                style={{
                  fontWeight: selected ? 'bold' : undefined,
                }}
                {...item}
              >
                {page}
              </button>
            )
          } else {
            children = (
              <button type="button" {...item}>
                {type}
              </button>
            )
          }

          return <li key={index}>{children}</li>
        })}
      </ul>
    </nav>
  )
}

const list = css`
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
`

propsを追加する

Pagination コンポーネントの props に pageCount(ページ総数)と currentPage(現在のページ番号)を追加する

Pagination.tsx
+ import { FC } from 'react'
  import usePagination from '@mui/material/usePagination'
  import { css } from '@emotion/react'

+ type PaginationProps = {
+   pageCount: number
+   currentPage: number
+ }

- export const Pagination = () => {
+ export const Pagination: FC<PaginationProps> = ({ pageCount, currentPage }) => {
    const { items } = usePagination({
-     count: 10,
+     count: pageCount,
+     page: currentPage,
    })

    return (
      <nav>
        <ul css={list}>
          {items.map(({ page, type, selected, ...item }, index) => {
            let children = null

            if (type === 'start-ellipsis' || type === 'end-ellipsis') {
              children = '…'
            } else if (type === 'page') {
              children = (
                <button
                  type="button"
                  style={{
                    fontWeight: selected ? 'bold' : undefined,
                  }}
                  {...item}
                >
                  {page}
                </button>
              )
            } else {
              children = (
                <button type="button" {...item}>
                  {type}
                </button>
              )
            }

            return <li key={index}>{children}</li>
          })}
        </ul>
      </nav>
    )
  }

  const list = css`
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
  `
Pagination.tsx
  import { FC } from 'react'
+ import Link from 'next/link'
+ import { useRouter } from 'next/router'
  import usePagination from '@mui/material/usePagination'
  import { css } from '@emotion/react'

  type PaginationProps = {
    pageCount: number
    currentPage: number
  }

  export const Pagination: FC<PaginationProps> = ({pageCount, currentPage}) => {
+   const router = useRouter()
    const { items } = usePagination({
      count: pageCount,
      page: currentPage,
    })

    return (
      <nav>
        <ul css={list}>
          {items.map(({ page, type, selected, ...item }, index) => {
            let children = null

            if (type === 'start-ellipsis' || type === 'end-ellipsis') {
              children = '…'
            } else if (type === 'page') {
+             const query = { ...router.query, ...{ page } }
+             if (page === 1) {
+               delete query.page
+             }
              children = (
-               <button
-                 type="button"
-                 style={{
-                   fontWeight: selected ? 'bold' : undefined,
-                 }}
-                 {...item}
-               >
-                 {page}
-               </button>
+               <Link
+                 href={{
+                   pathname: router.pathname,
+                   query
+                 }}
+                 passHref
+               >
+                 <a>
+                   <button
+                     type="button"
+                     style={{
+                       fontWeight: selected ? 'bold' : undefined,
+                     }}
+                     {...item}
+                   >
+                     {page}
+                   </button>
+                 </a>
+               </Link>
              )
-           } else {
-             children = (
-               <button type="button" {...item}>
-                 {type}
-               </button>
+           } else if (type === 'previous') {
+             const query = { ...router.query, ...{ page: currentPage - 1 } }
+             if (page === 1) {
+               delete query.page
+             }
+             children = (
+               <Link
+                 href={{
+                   pathname: router.pathname,
+                   query
+                 }}
+                 passHref
+               >
+                 <a>
+                   <button type="button" {...item}>
+                     {type}
+                   </button>
+                 </a>
+               </Link>
+             )
+           } else if (type === 'next') {
+             const query = { ...router.query, ...{ page: currentPage + 1 }
+             children = (
+               <Link
+                 href={{
+                   pathname: router.pathname,
+                   query
+                 }}
+                 passHref
+               >
+                 <a>
+                   <button type="button" {...item}>
+                     {type}
+                   </button>
+                 </a>
+               </Link>
              )
            }

            return <li key={index}>{children}</li>
          })}
        </ul>
      </nav>
    )
  }

  const list = css`
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
  `

あとはお好みのスタイルをあてるだけ。

Discussion