Next.js × microCMSでページング「react-paginate」を使っての実装方法
2021年9月21日
2022年4月15日
目次
前提
必要なもの
microcms-js-sdk
npm
npm install microcms-js-sdk
yarn
yarn add microcms-js-sdk
react-paginate
npm
npm install react-paginate
yarn
yarn add react-paginate
PagingComponentを作る(styled-componentsと合わせて使う)
Pagination.tsx
import React from 'react'
import Router from 'next/router'
import ReactPaginate from 'react-paginate'
import styled from 'styled-components'
import { LIST_LIMIT } from '~/const/api'
const PageContainer = styled.div`
margin: 24px 0;
& ul {
display: flex;
justify-content: center;
font-size: 14px;
}
& li {
list-style-type: none;
margin: 0 4px;
cursor: pointer;
transition: opacity 0.2s ease;
&:hover {
opacity: 0.6;
}
}
& .previous a, .next a {
display: block;
margin-top: 15px;
width: 16px;
height: 16px;
text-indent: -1000px;
transform: rotate(45deg);
overflow: hidden;
}
& .previous a {
border-left: 1px solid #39c;
border-bottom: 1px solid #39c;
}
& .next a {
border-top: 1px solid #39c;
border-right: 1px solid #39c;
}
& li.selected {
pointer-events: none;
}
& li.selected a {
background-color: #39c !important;
color: #fff !important;
}
& li:not(.previous,.next) a {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
border: 1px solid #39c;
color: #39c;
}
& li.break a {
border: none;
}
.disabled {
opacity: 0.2;
pointer-events: none;
}
@media (max-width: 600px) {
& .previous a, .next a {
margin-top: 13px;
width: 8px;
height: 8px;
}
& li:not(.previous,.next) a {
width: 32px;
height: 32px;
}
}
`
interface PaginationProps {
rootPath?: string
totalCount: number
currentNum: number
}
const ONE_PAGE_DISPLAY_USERS = LIST_LIMIT
const LAST_DISPLAY_SIZE = LIST_LIMIT
const AROUND_DISPLAY_PAGES = Math.floor(LIST_LIMIT / 2)
const Pagination: React.FC<PaginationProps> = props => {
const handlePaginate = (selectedItem: { selected: number }) => {
if(selectedItem.selected === 0) {
Router.push('/')
} else {
Router.push(`${props.rootPath || ''}/page/${selectedItem.selected}`)
}
}
return (
<PageContainer>
<ReactPaginate
pageCount={Math.ceil(props.totalCount / ONE_PAGE_DISPLAY_USERS)}
initialPage={props.currentNum}
marginPagesDisplayed={LAST_DISPLAY_SIZE}
pageRangeDisplayed={AROUND_DISPLAY_PAGES}
onPageChange={handlePaginate}
/>
</PageContainer>
)
}
export default Pagination
styled-componentsを使う場合は下記に注意。
下記のように書くやり方ですと、できないようなので、PageContainer
で囲ってあげています。
https://github.com/AdeleD/react-paginate/issues/321
react-paginateで、使っているオプション解説
名前 | タイプ | 説明 |
---|---|---|
pageCount | Number | 必須。総ページ数 |
pageRangeDisplayed | Number | 必須。表示されるページの範囲 |
marginPagesDisplayed | Number | 必須。余白に表示するページ数 |
initialPage | Number | デフォルトの選択ページ位置 |
onPageChange | Function | ページをクリックした時に呼び出す関数 |
ページ用のコンポーネント作成(ページングを使う側)
api.ts
export const LIST_LIMIT = 10
export const LIST_OFFSET = 10
client.js
import { createClient } from 'microcms-js-sdk';
export const client = createClient({
serviceDomain: process.env.DOMAIN,
apiKey: process.env.API_KEY || '',
})
[page].tsx(一部抜粋)
import type { NextPage } from 'next'
import { LIST_LIMIT, LIST_OFFSET } from '../const/api'
import Pagination from '../components/common/Pagination'
interface Home {
entries: EntriesApi
page: number
}
const Home: NextPage<Home> = (props) => {
return (
<main>
<article>
<div>
<Pagination {...{
totalCount: props.entries.totalCount,
currentNum: props.page
}}/>
</div>
</article>
</main>
)
}
import { client } from '~/utils/client';
import { GetStaticProps, GetStaticPaths } from 'next'
export const getStaticPaths: GetStaticPaths = async () => {
const { totalCount } = await client.get<EntriesApi>({
endpoint: 'entries',
queries: {
offset: 0,
limit: 0,
fields: 'id'
}
})
const paths = [...Array(Math.ceil(totalCount / LIST_LIMIT))]
.map((_, i) => i)
.map((page) => `/page/${page}`)
return { paths, fallback: false }
}
export const getStaticProps: GetStaticProps = async ({ params }) => {
const page = parseInt(String(params?.page)) || 0;
const [entries] = await Promise.all([
client.get<EntriesApi>({
endpoint: 'entries',
queries: {
orders: '-publishedAt',
offset: page * LIST_OFFSET,
limit: LIST_LIMIT,
fields: 'id,title,categories,tags,publishedAt,image,description'
}
})
])
return {
props: {
entries,
page
},
};
};
getStaticPathsではoffset: 0,limit: 0で取得しています。
ここではtotalCount
が欲しいだけなので、実際にリストは取得しないようにしています。page * LIST_OFFSET
としてるところが少し注意です。
microcmsの仕様的にはoffsetの位置からlimitの位置までのものを取得という考え方です。
ですのでpage数をそのまま与えても、うまくいかないのでページ数かける表示されてる数を渡してあげることで意図した動作になります。
例: ページ数(2) * 表示数(10) = 20 〜 リストのリミット数(10)が表示
interface EntriesApi
export interface ListApi {
totalCount: number
offset: number
limit: number
}
export interface EntriesApi extends ListApi {
contents: Entry[]
}
参考
まとめ
ページング処理も毎回つくると大変なので横着できるところはプラグインを使ってサクッと作っていけると良いですね!