Next.js、muiを用いたページネーション

こんにちは！watnowの神宮です。
今回はmuiを用いたページネーションについて書かせてもらいます！
DBはsupbaseを使っています！
いくつか問題点はある実装だと思いますが、読んでもらえれば幸いです！


## 1.MUIをインストールする。
```
npm install @mui/material @emotion/react @emotion/styled
```
## 2.Pagenationをとりあえずつくる

```pags.tsx
"use client";
import Pagination from '@mui/material/Pagination';


const Pagenation: React.FC = ()=>{
    
    return(
        <Pagenation />   
    );

};
default export Pagenation

```

こんな感じのやつが表示される
![スクリーンショット 2024-12-02 15.35.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940863/bdc3a6b2-4e95-895c-fecd-ac74e113307d.png)


## 3.paginationをカスタマイズ
```page.tsx
"use client";
import Pagination from '@mui/material/Pagination';

const Pagenation: React.FC = () =>{
    return (
        <Pagenation
            count={8}　//表示されるページ数が8になる
            renderItem={(item)=>(   //ページネーションの両サイドにある<,>を矢印に
                <PaginationItem
                    {...item}
                    slots = {{
                        previous:()=> <div>←</div>,
                        next: ()=> <div>→</div>,
                    }}
                />
            )}
        />
    );
};
default export Pagenation
```

count
→ページネーションの総ページ数を決める

renderItem
→各ページアイテムをどのように表示するかを決める。
関数を受け取ってPaginationItemを表示。

※各ページアイテムは以下の写真参照
![スクリーンショット 2024-12-02 16.33.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940863/ad8fa455-7698-07be-f356-08f18b737c2d.png)


item
→各ページアイテムの情報を持っているオブジェクト

PaginationItem
→ページアイテムを表すコンポーネント

{...item}
→itemオブジェクトが持つプロパティをPagenationItemに渡している。

slots
→下記の~~~の部分を変えることで,ページネーションにある両端のボタンを変えることができる。
```page.tsx
slots={{
    previoust:()=>  ~~
    next: ()=> ~~~
}}
```


## 4.ページごとに表示されるものを変える。

ここからの僕の実装は問題ありです。
とりあえず実装したいんだ！という方の参考になれば幸いです！

まず、流れを説明します。

useStateで現在のページ数、画面に表示したいもののデータを保持
↓
ページネーションがクリックされたらページ数を+1
↓
ページ数が変わったら、データをDBから取ってきて、画面に表示したいもののデータを更新。



### 必要なstateを用意する
```page.tsx
const [services, setServices] = useState<Service[]>([]); //表示したいもののデータ
const [pageNum,setPageNum] = useState(1); //現在のページ数
const [totalPageNum,setTotalPageNum] = useState(0); //総ページ数

```
### ページネーションがクリックされた時の処理を用意する
pagaNumを次に進めます！！

```page.tsx
    const scrollTop =()=>{
        window.scrollTo({ top:0 });
    };
  
    const handlePageChange =(_: React.ChangeEvent<unknown>, nextPage:number)=>{
        setPageNum(nextPage);
        scrollTop();
        console.log(nextPage);
      }
    return (
         <Pagenation
             //省略
                onChange={handlePageChange}
            //省略
        />
    );
```

_ 
→このアンダースコアは変数名。Js,Tsで使わない変数を_で表す

React.cahnageEvent
→ReactのChangeイベントを表す

unknow
→unknowはどのhtml要素から発生したのかわからない時に使う。

scrollTop()
→ページネーションをクリックしたときにページ上部へ移動するため。




nexPage: Paginationコンポーンネント勝手に計算して、次に表示するページを渡してくれます！




### 現在のpage数が変わった際の処理を用意

pageNumが変わったときに、ページ数に応じてデータを取得。

```page.tsx
useEffect(()=>{
    const start = (pageNum-1) * servicesPerPage;
    const end = start + servicesPerPage - 1;

    const fetchService = async () => { //ページ数に応じてデータを取得
      const { data, error } = await supabase
        .from("services")
        .select("id,name,image")
        .order("id", { ascending: true })
        .range(start,end)
      if (error) {
        console.error("Error fetching events:", error);
      } else {
        console.log("Fetched data:", data);
        setServices((data as Service[]) || []);
      }
      setLoading(false);
    };

    fetchService()
  },[page])
```


start, end 
→DBから取ってくるデータの最初の範囲を選ぶ。

.order("id", { ascending: true })
→idを使って昇順で取得

## 初回レンダリング時に表示されるデータ、ページに表示したいデータの総数を取得

```page.tsx
useEffect(() => {
    const start = (pageNum-1) * servicesPerPage;
    const end = start + servicesPerPage - 1;

    const fetchService = async () => {
      const { data, error } = await supabase
        .from("services")
        .select("id,name,image")
        .order("id", { ascending: true })
        .range(start,end);

      if (error) {
        console.error("Error fetching events:", error);
      } else {
        console.log("Fetched data:", data);
        setServices((data as Service[]) || []);
      }

    };


    const fetchEventCount = async () => {
      const { count, error:countError} = await supabase
        .from("services")
        .select('*',{count:'exact',head: true});
      console.log("カウントしている",count);
      
      if (countError) {
        console.error("Error fetching event count",countError)
        return;
      }
      setTotalServiceNum(count || 0);
    }

    fetchService();
    fetchEventCount();
  }, []);

```
最初の関数は同じものを2回書いています。よくないです！！

count:'exact'
→データ数を取得

head: true
→head:trueにすることでデータをとらずにデータ数のみ取得



## 5.ページネーションの表示ページ数を変える
```page.tsx

      <Pagination
        className={styles.pageNation}
        count={Math.ceil(totalServicesCount / servicesPerPage)}
        onChange={handlePageChange}
        renderItem={(item) => (       
        
      />
```

Math.ceil
→jsのメソッドで小数点以下を切りあげる。

上記のコードで完成になります！！

## 最後に

次回ページネーションを作る機会があれば、よりスムーズに作れるようになったと思います。

このように一つずつ自分ができることを増やしていきたです。

ここまで読んでくださりありがとうございます。
アドバイスがあれば気軽におっしゃってください！！！