はじめに
Next.js app routerのチュートリアルの第11章-2のアウトプットをします。
前の記事
【01】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/af58da3d20cbc790e767
【02】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/edf450b3ee135e83d1e8
【03】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/612221eac233aa9cbb74
【04】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/62f9beccbfe36eaf7f90
【05】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/8b71b1d1df7c9435a9c9
【06】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/58130c3cfbaf8a573de2
【07】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/2c2da0f8071e60454679
【08】Next.js app routerのチュートリアルやってみる
https://qiita.com/naoyuki2/items/45f45fcb9cc14506f79f
【09】Next.js app routerのチュートリアルやってみる(loading.tsxとSuspenseでストリーミング)
https://qiita.com/naoyuki2/items/717694288ec6017a3af2
【10】Next.js app routerのチュートリアルやってみる(部分的な事前レンダリング)
https://qiita.com/naoyuki2/items/8062f755b0679fe925b1
【11-1】Next.js app routerのチュートリアルやってみる(URLパラメーターを利用した検索機能)
第11-2章 ページネーション
この章では以下を学びました。
-
ページネーション
の実装
第11章の後編です。
ページネーションの追加
前回検索機能を実装できましたが、現在一度に6つまでしかデータを表示できません。
fetchFilterdInvoices
関数は一度に6つしかデータを取ってこないからです。
const invoices = await fetchFilteredInvoices(query, currentPage);
const ITEMS_PER_PAGE = 6
export async function fetchFilteredInvoices(
query: string,
currentPage: number,
) {
noStore()
const offset = (currentPage - 1) * ITEMS_PER_PAGE
try {
const invoices = await sql<InvoicesTable>`
SELECT
// ....
LIMIT ${ITEMS_PER_PAGE} OFFSET ${offset}
`
return invoices.rows
} catch (error) {
console.error('Database Error:', error)
throw new Error('Failed to fetch invoices.')
}
}
そのため、ページネーションを実装して、すべてのデータを見れるようにしましょう。
前回の検索機能と同じように、URLパラメーターを利用してページネーションを実現します。
合計ページ数を取得
合計ページ数が分からないことにはページネーション
は実装できません。
fetchInvoicesPages
という関数をimport
します。
そして、この関数にquery
(検索キーワード)を渡すことで、合計ページ数が取得できます。
// ...
+import { fetchInvoicesPages } from '@/app/lib/data';
export default async function Page({
searchParams,
}: {
searchParams?: {
query?: string,
page?: string,
},
}) {
const query = searchParams?.query || '';
const currentPage = Number(searchParams?.page) || 1;
+ const totalPages = await fetchInvoicesPages(query);
return (
// ...
);
}
そして、合計ページ数totalPages
を<Pagination />
コンポーネントに渡しましょう。
<Pagination totalPages={totalPages} />
<Pagination />
コンポーネントに移動しましょう。
そして、フックを利用しURLとパラメータを取得します。
'use client';
import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline';
import clsx from 'clsx';
import Link from 'next/link';
import { generatePagination } from '@/app/lib/utils';
import { usePathname, useSearchParams } from 'next/navigation';
export default function Pagination({ totalPages }: { totalPages: number }) {
+ const pathname = usePathname();
+ const searchParams = useSearchParams();
+ const currentPage = Number(searchParams.get('page')) || 1;
// ...
}
そして、次のページや前のページなどのURLが変化する場合にcreatePageURL
という関数を呼び出します。
これによって、URLパラメータのpage
の部分を変更させています。
'use client';
import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline';
import clsx from 'clsx';
import Link from 'next/link';
import { generatePagination } from '@/app/lib/utils';
import { usePathname, useSearchParams } from 'next/navigation';
export default function Pagination({ totalPages }: { totalPages: number }) {
const pathname = usePathname();
const searchParams = useSearchParams();
const currentPage = Number(searchParams.get('page')) || 1;
const createPageURL = (pageNumber: number | string) => {
const params = new URLSearchParams(searchParams);
params.set('page', pageNumber.toString());
return `${pathname}?${params.toString()}`;
};
// ...
}
そして、前回実装したhandleSearch
関数のところに、ページ番号を1にリセットする処理を書きましょう。
'use client';
import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { useDebouncedCallback } from 'use-debounce';
export default function Search({ placeholder }: { placeholder: string }) {
const searchParams = useSearchParams();
const { replace } = useRouter();
const pathname = usePathname();
const handleSearch = useDebouncedCallback((term) => {
const params = new URLSearchParams(searchParams);
+ params.set('page', '1');
if (term) {
params.set('query', term);
} else {
params.delete('query');
}
replace(`${pathname}?${params.toString()}`);
}, 300);
おわりに
useState
で管理しなくても実装できるものなんですね。
heroicon
がちょくちょく出てくるからいつか調べよう。
次の記事