次のようなページネーション機能付きのグリッドを作成していく。
完成イメージ
グリッドで表示されるカードはこのページで作成したBlogCardコンポーネントを使用している。
Material UIでページ情報を表示するカード型のリンクを作成する
Material UIにはGridというコンポーネントが存在するがその中身はFlexで作成されているため今回は使用しない。
export function CardGrid(props:{children:ReactNode;}) { return ( <Box display='grid' gap={2} gridTemplateColumns='repeat(auto-fit, minmax(310px, 1fr))' > {props.children} </Box> );};
gridTemplateColumns
を'repeat(auto-fit, minmax(310px, 1fr))'
とすることで自動で良い感じに幅を調整してくれる。
repeat(auto-fit, ... )
を設定することで画面幅に応じて、できるかぎり列数が多くなるように配置するようになる。
minmax(310px, 1fr)
は最小幅が310pxで、最大幅は残りの部分すべてということを意味している。
こうすることで、gridの子要素の幅が310px以下になるような場合は列数が自動で減るようになり、もし余白がでるようならば伸長するようになる。
カスタムフックのuseCardsPagenationを作る。
引数としてカードとして表示するブログのメタ情報の配列を受け取るようにしている。
たとえば、次のようなタイトルや更新日時の情報が含まれている配列。
[ { title: 'Material UIの使い方', datepublished: '2023-10-11', datemodified: '2023-10-12', tags:['react','mui'], path:'/howto-mui' }, ...];
この配列からカードとして表示するぶんだけを切り出すようにする。
import { useState } from "react";import { useMediaQuery } from "@mui/material";const useCardsPagenation = (blogMetaList) => { // 現在、何ページ目を表示しているかのstate const [pageIndex, setPageIndex] = useState(1); // 画面サイズがxl以上なら1ページにつき12枚のカードを表示させる // それ未満なら9枚のカードを表示させる const isLarge = useMediaQuery((theme: any) => theme.breakpoints.up('xl')); const cardCountByPage = isLarge ? 12 : 9; // 総ページ数 const pageCount = Math.ceil(blogMetaList.length / cardCountByPage); // カードとして表示するぶんだけのブログ情報を切り出す const firstBlogIndex = (pageIndex - 1) * cardCountByPage; const displayedBlogMetaList = blogMetaList.slice(firstBlogIndex, firstBlogIndex + cardCountByPage); // ページ番号を変更するときに実行する関数 const changePageIndex = (_: any, value: number) => { setPageIndex(value); // ページを変更したら上にスクロールさせる window.scroll({ top: 0, behavior: 'smooth' }); }; return { pageIndex, displayedBlogMetaList, changePageIndex, pageCount };};
今まで作成したものを組み合わせてBlogCardsコンポーネントを作る。
'use client'; //Next.jsの場合は必要import { useState } from "react";import { Box, Paginationy } from "@mui/material";export function BlogCards(props: { blogMetaList }) { const { pageIndex, pageCount, displayedBlogMetaList, changePageIndex } = useCardsPagenation(props.blogMetaList); return ( <div> <CardGrid> {displayedBlogMetaList.map((blogMeta) => <BlogCard key={blogMeta.path} {...blogMeta} />)} </CardGrid> <Pagination size="large" shape="rounded" color="secondary" count={pageCount} page={pageIndex} onChange={changePageIndex} sx={{ marginY: 4, '.MuiPagination-ul': { justifyContent: 'center' } }} /> </div> );};
Pagination
のsxでjustifyContent: 'center'
を指定することで中央寄せしている。
ブログのメタ情報を取得して上記のコンポーネントに流し込む。
サーバーから取得したり、Next.jsでMDXを利用している場合はreaddirSyncでMDXファイルから取得したりなど。
export default function Page() { // ブログのメタ情報を取得している const blogMetaList = getBlogMetaList(); return ( <div> <Typography component='h1'>'すべての記事'</Typography> <BlogCards blogMetaList={blogMetaList} /> </div> );}