ロゴWeb開発ブログ

Material UIで自動で列を調整してくれるグリッドレイアウトを作り、ページネーション機能を付ける

更新
作成
  • 使用したバージョン
  • @mui/material 5.13.6

次のようなページネーション機能付きのグリッドを作成していく。

完成イメージ
完成イメージ

グリッドで表示されるカードはこのページで作成した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>
);
}