分页
RTK Query 不包含任何内置的分页行为。但是,RTK Query 使得与标准的基于索引的分页 API 集成变得非常简单。这是您需要实现的最常见的分页形式。
分页食谱
设置一个接受页面arg
的端点
- TypeScript
- JavaScript
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
src/app/services/posts.js
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (builder) => ({
listPosts: builder.query({
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'
标签,并失效
任何应该影响它的突变的相应标签。
- TypeScript
- JavaScript
失效分页查询缓存的示例
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' },
],
}),
}),
})
失效分页查询缓存的示例
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
export const postApi = createApi({
reducerPath: 'postsApi',
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
listPosts: build.query({
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', id })),
{ type: 'Posts', id: 'PARTIAL-LIST' },
]
: [{ type: 'Posts', id: 'PARTIAL-LIST' }],
}),
deletePost: build.mutation({
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
,但是当您向前移动时,我们将使用下一个/上一个按钮作为获取指示器,同时执行任何非缓存查询。当您返回时,缓存的数据将立即提供。