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 提供标签,并且不会失效以触发重新获取数据。结果,当前页面上应该向上移动一个项目的项目将不会这样做,并且项目的总数和/或页面数可能不正确。

克服此问题的一种策略是确保delete 突变始终失效分页查询,即使已删除的项目当前未在该页面上提供。我们可以利用使用抽象标签 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' },
],
}),
}),
})

通用分页示例

在以下示例中,您将在初始查询中看到Loading,但是当您向前移动时,我们将使用下一个/上一个按钮作为获取指示器,同时执行任何非缓存查询。当您返回时,缓存的数据将立即提供。