Usage > Pagination: suggestions for handling paginated data">Usage > Pagination: suggestions for handling paginated data">
跳至主要內容

分頁

RTK Query 沒有包含任何內建的分頁行為。不過,RTK Query 讓與標準的基於索引的分頁 API 整合變得簡單。這是您最常需要實作的分頁形式。

分頁範例

設定一個端點來接受頁面 arg

src/app/services/posts.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
interface ListResponse<T> {
page: number
per_page: number
total: number
total_pages: number
data: T[]
}

export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (builder) => ({
listPosts: builder.query<ListResponse<Post>, number | void>({
query: (page = 1) => `posts?page=${page}`,
}),
}),
})

export const { useListPostsQuery } = api

透過遞增 page 狀態變數來觸發下一頁

src/features/posts/PostsManager.tsx
const PostList = () => {
const [page, setPage] = useState(1)
const { data: posts, isLoading, isFetching } = useListPostsQuery(page)

if (isLoading) {
return <div>Loading</div>
}

if (!posts?.data) {
return <div>No posts :(</div>
}

return (
<div>
{posts.data.map(({ id, title, status }) => (
<div key={id}>
{title} - {status}
</div>
))}
<button onClick={() => setPage(page - 1)} isLoading={isFetching}>
Previous
</button>
<button onClick={() => setPage(page + 1)} isLoading={isFetching}>
Next
</button>
</div>
)
}

分頁查詢的自動重新擷取

使用標籤失效來執行 RTK Query 的自動重新擷取是很常見的用例。

在將此與分頁結合時,一個潛在的陷阱是,你的分頁查詢可能在任何給定的時間只提供一個部分清單,因此不會為當前未顯示的頁面上的實體 ID 提供標籤。如果刪除特定實體落在較早的頁面,則分頁查詢不會提供該特定 ID 的標籤,並且不會失效以觸發重新擷取資料。因此,當前頁面上應該向上移動一個項目的項目將不會這樣做,並且項目和/或頁面的總數可能不正確。

克服此問題的策略是確保刪除突變總是使分頁查詢失效,即使已刪除的項目當前未在該頁面上提供。我們可以利用使用抽象標籤 ID 的進階失效的概念,透過在分頁查詢中提供具有'PARTIAL-LIST' ID 的'Posts'標籤,並使任何應影響它的突變對應標籤失效來做到這一點。

使分頁查詢快取失效的範例
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
interface ListResponse<T> {
page: number
per_page: number
total: number
total_pages: number
data: T[]
}

export const postApi = createApi({
reducerPath: 'postsApi',
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
listPosts: build.query<ListResponse<Post>, number | void>({
query: (page = 1) => `posts?page=${page}`,
providesTags: (result, error, page) =>
result
? [
// Provides a tag for each post in the current page,
// as well as the 'PARTIAL-LIST' tag.
...result.data.map(({ id }) => ({ type: 'Posts' as const, id })),
{ type: 'Posts', id: 'PARTIAL-LIST' },
]
: [{ type: 'Posts', id: 'PARTIAL-LIST' }],
}),
deletePost: build.mutation<{ success: boolean; id: number }, number>({
query(id) {
return {
url: `post/${id}`,
method: 'DELETE',
}
},
// Invalidates the tag for this Post `id`, as well as the `PARTIAL-LIST` tag,
// causing the `listPosts` query to re-fetch if a component is subscribed to the query.
invalidatesTags: (result, error, id) => [
{ type: 'Posts', id },
{ type: 'Posts', id: 'PARTIAL-LIST' },
],
}),
}),
})

一般分頁範例

在以下範例中,你會在初始查詢中看到載入中,但當你前進時,我們會使用下一頁/上一頁按鈕作為擷取指標,同時執行任何非快取查詢。當你返回時,快取資料將立即提供。