はじめに
皆様こんにちは。
AMDlab Webエンジニアの塚田です。
最近Next.jsを使い始めました。その中でuseInfiniteQueryというものが便利に
感じたので今回ご紹介したいと思います。
Next.js
Reactをベースに開発された、フロントエンドフレームワークです。
useInfiniteQuery
useInfiniteQueryとは TanStack Query から提供されているreactの機能の一つで、
「無限スクロール」を簡単に実装することができます。
機能としては以下の流れで無限スクロールを実現しています。
- useInfiniteQuery を呼び、最初のデータを受け取る
- getNextPageParam で次のデータを取得するためのパラメータを返却する
※今回の実装ではページ番号のみ返却するようにしています。 - hasNextPageというフラグで次のデータがあるか確認します。
- 3.がtrueの場合、fetchNextPage を呼び、次のデータを取得する
無限スクロールの実装
開発環境
macOS Venture 13.0.1
Next.js 14.2.3
mysql 8.0
Next.js のルーティングにはPage Routerと App Router がありますが
今回はApp Routerを選択しました。以下フォルダ構成です。
1 2 3 4 5 6 7 8 9 |
test-sample └── next-js-sample ├── app │ ├── api │ │ └── test-api │ │ └── route.ts │ └── page.tsx └── components └── testInfiniteScroll.tsx |
mysql についてはdockerを利用してコンテナ化しました。dockerのコンテナ化等については
公式サイトを参照ください。
今回使うテーブルは以下のものになります。
1 |
CREATE TABLE test_table (id INT AUTO_INCREMENT, name TEXT, PRIMARY KEY (id)); |
まずtest_tableテーブルからデータを取得するAPIを用意します。
next-js-sample/app/api/test-api の下にroute.tsを配置します。
以下のコードを記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// next-js-sample/app/api/test-api/route.ts import { NextRequest, NextResponse } from "next/server"; import mysql from "mysql2/promise"; // test_tableのデータ取得API export async function GET(req: NextRequest) { const { searchParams } = new URL(req.url); const page = searchParams.get('page') ?? '0'; const offset = 100 * Number(page); const connection = await mysql.createConnection({ host: process.env.MYSQL_HOST, database: process.env.MYSQL_DATABASE, user: process.env.MYSQL_USER, password: process.env.MYSQL_PASSWORD, }); const query = `SELECT * FROM test_table LIMIT 100 OFFSET ${mysql.escape(offset)}`; const [rows] = await connection.execute(query); connection.end(); return NextResponse.json(rows); } |
useInfiniteQueryを利用した無限スクロールを実装していきます。スクロールのUIとして
react-infinite-scrollerのInfiniteScrollを利用します。このコンポーネントのpropsである
loadMoreに fetchNextPage メソッドを設定します。また hasMore にhasNextPage
フラグを設定します。
hasMore のhasNextPageがtrueの時 loadMoreで設定したfetchNextPageメソッドに
よりデータが取得され、スクロール内のデータが更新されます。
他にもInfiniteScrollにはpropsがあるので調べてみてください。
以下useInfiniteQueryとInfiniteScrollを使用しているコンポーネントのコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
// next-js-sample/components/testInfiniteScroll.tsx import { useInfiniteQuery } from "@tanstack/react-query"; import React from "react"; import InfiniteScroll from "react-infinite-scroller"; type RowData = { id: number; name: string; }; type PageParams = { pageParam: number; }; const TestInfiniteScroll: React.FC = () => { const fetchTestApi = async ({ pageParam }: PageParams) => { const res = await fetch(`/api/test-api?page=${pageParam}`); if (!res.ok) { throw new Error("Failed to fetch testTable"); } // コメントを外すとLoding...状態を画面上で確認できます。 // await new Promise((resolve) => setTimeout(resolve, 1000)); return res.json(); }; const { data, error, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage, status, } = useInfiniteQuery({ queryKey: ["test-api"], queryFn: fetchTestApi, initialPageParam: 0, getNextPageParam: (lastPage, allPages) => { // ここのページング処理は各自の使用に合わせて修正してください。 if (Array.isArray(lastPage)) { if (lastPage.length === 100) { return allPages.length; } } return undefined; }, }); return status === "pending" ? ( <p>Loading...</p> ) : status === "error" ? ( <p>Error: {error.message}</p> ) : ( <> <InfiniteScroll loadMore={() => fetchNextPage()} loader={<div key={0}>Loading...</div>} hasMore={hasNextPage} > {data.pages.map((page: RowData[], i) => ( <React.Fragment key={i}> {page.map((row) => ( <p key={row.id}>{row.name}</p> ))} </React.Fragment> ))} </InfiniteScroll> </> ); }; export default TestInfiniteScroll; |
Next.jsのルーティングによりfetch の第1引数 api/test-api とすることで
next-js-sample/api/test-api/route.ts のGETメソッドが呼び出され、
test_tableテーブルからデータを取得することができます。
最後に初期表示のpage.tsxのコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// next-js-sample/app/page.tsx "use client"; import TestInfiniteScroll from "@/components/testInfiniteScroll"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; const queryClient = new QueryClient(); const TestInfiniteScrollPage = () => { return ( <QueryClientProvider client={queryClient}> <TestInfiniteScroll /> </QueryClientProvider> ); }; export default TestInfiniteScrollPage; |
useInfiniteQuery(useQueryも)を使用する場合には上記のように
QueryClient、QueryClientProviderを使用する必要があります。
※ルーティングにapp routerを選択している為、”use client” をつけないと
エラーとなるのでご注意ください。
今回は100件ずつ取得しています。次のデータはLoading…が表示されたのち、
表示されます。
おわりに
Next.js(React)の無限スクロールの実装はかなり簡単に実装できることをご紹介させていただきました。
他にも便利な機能が満載のようなので勉強して業務等に活用していきたいと思います。
COMMENTS