TanStack query を使うと、データの取得や、更新時のキャッシュ、古いデータ周りのことを簡単に扱うことができます。この記事では、create-react-app でアプリ作成後 TanStack query で無限読み込みを実装しています。
アプリ作成とパッケージをインストール
react アプリを作成します。
create-react-app アプリ名
TanStack query パッケージをインストールします。
npm install @tanstack/react-query
ファイル構成
src//
|-- api//
| `-- comments.js
|-- App.js
`-- index.js
TanStack query を使う
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient()
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
);
11, 13: QueryClientProvider で App コンポーネントを囲います。
サーバーからデータを取得
let Comments = [
{ id: 1, comment: "Comment 1" },
{ id: 2, comment: "Comment 2" },
{ id: 3, comment: "Comment 3" },
{ id: 4, comment: "Comment 4" },
{ id: 5, comment: "Comment 5" },
]
export const getComment = (pageParam) => {
return delay().then(() => {
let res = {
Comments: [Comments.find((r) => r.id === pageParam)],
page: pageParam,
totalPages: Comments.length
}
return res;
}).catch((error) => {
return Promise.reject({ error })
})
}
async function delay(ms = 500) {
return new Promise(resolve => setTimeout(resolve, ms))
// return new Promise((resolve, reject) => setTimeout(() => reject("error message"), ms));
}
サーバー側はページ番号を受け取り、12: ページ番号に対するデータ、13: 現在のページ番号、14: データの合計数を返しています。
無限読み込み機能
実装には、 useInfiniteQuery()
を使います。useQuery()
にオプションや戻り値が追加されています。useInfiniteQuery
import { useInfiniteQuery } from '@tanstack/react-query'
import { getComment } from './api/comments';
function App() {
const query = useInfiniteQuery({
queryKey: ["comments"],
queryFn: ({ pageParam = 1 }) => getComment(pageParam),
getNextPageParam: (lastPage, allPages) => {
if (lastPage.page < lastPage.totalPages) {
return lastPage.page + 1;
} else {
return undefined;
}
},
refetchOnWindowFocus: false,
})
if (query.isLoading) return <h1>Loading...</h1>
if (query.isError) return <h1>{JSON.stringify(query.error)}</h1>
return (
<div className="App">
{query.data.pages.map(page =>
page.Comments.map(res => <div key={res.id}>{res.id} {res.comment}</div>)
)}
{/* {query.data.pages.flatMap(page => page.Comments.map(res => <div key={res.id}>{res.id} {res.comment}</div>))} */}
{query.hasNextPage && (
<button onClick={() => query.fetchNextPage()}>
{query.isFetchingNextPage ? "isLoading..." : "Load"}
</button>
)}
</div>
);
}
export default App;
7: 最初のページを表示させるために、pageParam = 1 を渡して読み込みます。
8: getNextPageParam
この関数は、最後のページオブジェクトと、全てのページのオブジェクトを受け取ります。ここでは、次の読み込みのための pageParam を単一の変数で返します。
12: 読み込むデータがない場合は、 undefined
を返します。
15: useQuery()
で使えたオプションも使えます。
22~24: useQuery()
と data の中身が異なっているので、展開方法も少し変わっています。
28: hasNextPage
読み込むデータがある場合に true を返します。
以上無限読み込みを実装する方法でした。