跳到主要内容

 

分页

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>加载中</div>
}

if (!posts?.data) {
return <div>没有帖子 :(</div>
}

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

自动重新获取分页查询

使用标签失效来执行 RTK Query 的自动重新获取是一个常见的用例。

当这与分页结合时的一个潜在陷阱是,你的分页查询可能只在任何给定的时间提供一个_部分_列表,因此不为当前未显示的页面上的实体 ID 提供 标签。如果一个特定的实体被删除,该实体位于一个较早的页面上,分页查询将不会为该特定 ID 提供标签,也不会被失效以触发重新获取数据。因此,应该向上移动一个项目的当前页面上的项目将不会这样做,项目的总数和/或页面可能是不正确的。

克服这个问题的策略是确保 删除 变异总是 使...失效 分页查询,即使被删除的项目当前并未在该页面上提供。我们可以利用高级失效与抽象标签 id的概念,通过在我们的分页查询中 提供 一个 'Posts' 标签与 'PARTIAL-LIST' ID,并对任何应该影响它的变异 使...失效 该相应的标签。

Example of invalidating cache for paginated queries
// file: api.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 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
? [
// 为当前页面中的每个帖子提供一个标签,
// 以及 'PARTIAL-LIST' 标签。
...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',
}
},
// 使这个 Post `id` 的标签失效,以及 `PARTIAL-LIST` 标签,
// 如果一个组件订阅了查询,那么会导致 `listPosts` 查询重新获取。
invalidatesTags: (result, error, id) => [
{ type: 'Posts', id },
{ type: 'Posts', id: 'PARTIAL-LIST' },
],
}),
}),
})

通用分页示例

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