自动重新获取
如在默认缓存行为下所见,当为查询端点添加订阅时,只有当缓存数据不存在时才会发送请求。如果存在,将提供现有数据。
RTK Query 使用一个 "缓存标签" 系统,自动为受突变端点影响的查询端点重新获取数据。这使得你可以设计你的 API,使得触发特定的突变将导致某个查询端点认为其缓存数据 无效 ,并在存在活动订阅时重新获取数据。
每个端点 + 参数组合都贡献其自己的 queryCacheKey
。缓存标签系统使得能够通知 RTK Query,特定的查询缓存已经 提供 特定的标签。如果触发了一个突变,该突变被认为 invalidate
了查询缓存已经 提供 的标签,那么缓存数据将被认为是 无效的 ,并在对缓存数据有活动订阅的情况下重新获取。
对于通过其他方式触发重新获取,请参见操作缓存行为。
定义
标签
对于 RTK Query,标签 只是你可以给特定的数据集合命名,以控制缓存和无效化行为以便重新获取。它可以被视为附加到缓存数据的 '标签',在突变后读取,以决定数据是否应该受到突变的影响。
在定义 api 时,标签在 tagTypes
参数中定义。例如,在一个既有 Posts
又有 Users
的应用中,你可能在调用 createApi
时定义 tagTypes: ['Post', 'User']
。
一个单独的 tag
有一个 type
,表示为 string
名称,和一个可选的 id
,表示为 string
或 number
。它可以被表示为一个简单的字符串(如 'Post'
),或者一个形状为 {type: string, id?: string|number}
的对象(如 [{type: 'Post', id: 1}]
)。
提供标签
一个 查询 可以让其缓存数据 提供 标签。这样做决定了哪个 '标签' 附加到查询返回的缓存数据上。
providesTags
参数可以是 string
的数组(如 ['Post']
)、{type: string, id?: string|number}
(如 [{type: 'Post', id: 1}]
),或者一个返回这样的数组的回调函数。该函数将结果作为第一个参数,响应错误作为第二个参数,原始传入 query
方法的参数作为第三个参数。注意,根据查询是否成功,结果或错误参数可能会未定义。
使标签无效
一个 变更 可以基于标签 使 特定的缓存数据 无效。这样做决定了哪些缓存数据将被重新获取或从缓存中移除。
invalidatesTags
参数可以是 string
的数组(如 ['Post']
)、{type: string, id?: string|number}
(如 [{type: 'Post', id: 1}]
),或者一个返回这样的数组的回调函数。该函数将结果作为第一个参数,响应错误作为第二个参数,原始传入 query
方法的参数作为第三个参数。注意,根据变更是否成功,结果或错误参数可能会未定义。
缓存标签
RTK Query 使用 '标签' 的概念来确定一个端点的变更是否意图 使 另一个端点的查询 提供 的一些数据 无效。
如果缓存数据正在被使无效,它将重新获取提供查询(如果组件仍在使用该数据)或从缓存中移除数据。
在定义 API 切片时,createApi
接受 tagTypes
属性的标签类型名称数组,这是 API 切片的查询可能提供的可能标签名称选项的列表。
下面的示例声明端点可能向缓存提供 'Posts' 和/或 'Users':
// 文件:types.ts noEmit
export interface Post {
id: number
name: string
}
export interface User {
id: number
name: string
}
// 文件:api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: '/',
}),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => '/posts',
}),
getUsers: build.query<User[], void>({
query: () => '/users',
}),
addPost: build.mutation<Post, Omit<Post, 'id'>>({
query: (body) => ({
url: 'post',
method: 'POST',
body,
}),
}),
editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
}),
}),
})
通过声明这些标签作为可能提供给缓存的内容,它使得单个变更端点能够声明它们是否影响缓存的特定部分,这与单个端点上的 providesTags
和 invalidatesTags
结合使用。
提供缓存数据
每个单独的 query
端点都可以让其缓存数据 提供 特定的标签。这样做可以在一个或多个查询端点的缓存数据和一个或多个变更端点的行为之间建立关系。
query
端点上的 providesTags
属性用于此目的。
在不同的 query
端点之间,提供的标签没有固有的关系。提供的标签用于确定是否应该 invalidate
端点返回的缓存数据,并重新获取或从缓存中移除。如果两个不同的端点提供相同的标签,它们仍然会贡献自己独特的缓存数据,这些数据可能会被来自变更的单个标签同时使无效。
下面的示例声明 getPosts
query
端点使用 providesTags
属性向缓存 提供
'Post'
标签。
// 文件:types.ts noEmit
export interface Post {
id: number
name: string
}
export interface User {
id: number
name: string
}
// 文件:api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: '/',
}),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => '/posts',
providesTags: ['Post'],
}),
getUsers: build.query<User[], void>({
query: () => '/users',
providesTags: ['User'],
}),
addPost: build.mutation<Post, Omit<Post, 'id'>>({
query: (body) => ({
url: 'posts',
method: 'POST',
body,
}),
}),
editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
}),
}),
})
为了更精细地控制提供的数据,提供的 tags
可以有关联的 id
。这使得可以区分 '特定类型的任何标签' 和 '特定类型的特定实例标签'。
下面的示例声明提供的帖子与端点返回的结果确定的特定 ID 相关联:
// 文件:types.ts noEmit
export interface Post {
id: number
name: string
}
export interface User {
id: number
name: string
}
// 文件:api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: '/',
}),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => '/posts',
providesTags: (result, error, arg) =>
result
? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']
: ['Post'],
}),
getUsers: build.query<User[], void>({
query: () => '/users',
providesTags: ['User'],
}),
addPost: build.mutation<Post, Omit<Post, 'id'>>({
query: (body) => ({
url: 'post',
method: 'POST',
body,
}),
}),
editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
}),
}),
})
注意,对于上面的示例,如果结果成功,尽可能使用 id
。在出现错误的情况下,没有提供结果,我们仍然认为它提供了通用的 'Post'
标签类型,而不是该标签的任何特定实例。
为了更强地控制无效化适当的数据,你可以为给定的标签使用一个任意的 ID,如 'LIST'
。请参阅 使用抽象标签 ID 的高级无效化 以获取更多详情。
使缓存数据无效
每个单独的变更(mutation)端点都可以使现有缓存数据的特定标签无效
。这样做可以在一个或多个查询端点的缓存数据和一个或多个变更端点的行为之间建立关系。
变更端点上的 invalidatesTags
属性用于此目的。
下面的示例声明 addPost
和 editPost
变更端点使用 invalidatesTags
属性使任何带有 'Post'
标签的缓存数据无效:
// 文件:types.ts noEmit
export interface Post {
id: number
name: string
}
export interface User {
id: number
name: string
}
// 文件:api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: '/',
}),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => '/posts',
providesTags: (result, error, arg) =>
result
? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']
: ['Post'],
}),
getUsers: build.query<User[], void>({
query: () => '/users',
providesTags: ['User'],
}),
addPost: build.mutation<Post, Omit<Post, 'id'>>({
query: (body) => ({
url: 'post',
method: 'POST',
body,
}),
invalidatesTags: ['Post'],
}),
editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
invalidatesTags: ['Post'],
}),
}),
})
对于上面的示例,这告诉 RTK Query 在调用并完成 addPost
和/或 editPost
变更后,任何带有 'Post'
标签的缓存数据都不再有效。如果一个组件在上述变更调用并完成后仍订阅了带有 'Post'
标签的缓存数据,它将自动重新获取,以便从服务器获取最新数据。
一个示例场景可能是这样的:
- 渲染一个组件,该组件使用
useGetPostsQuery()
钩子订阅该端点的缓存数据 - 触发
/posts
请求,服务器响应带有 ID 1、2 和 3 的帖子 getPosts
端点将接收到的数据存储在缓存中,并内部注册已提供以下标签:[
{ type: 'Post', id: 1 },
{ type: 'Post', id: 2 },
{ type: 'Post', id: 3 },
]- 触发
editPost
变更以修改特定帖子 - 完成后,RTK Query 内部注册
'Post'
标签现在已无效,并从缓存中移除先前提供的'Post'
标签 - 由于
getPosts
端点提供了现在缓存数据无效的'Post'
类型的标签,且组件仍订阅该数据,/posts
请求会自动再次触发,获取新数据并为更新的缓存数据注册新标签
为了更精细地控制无效的数据,无效的 tags
可以有关联的 id
,就像 providesTags
一样。这使得可以区分 '特定类型的任何标签' 和 '特定类型的特定实例标签'。
下面的示例声明 editPost
变更使特定实例的 Post
标签无效,使用在调用变更函数时传入的 ID:
// 文件:types.ts noEmit
export interface Post {
id: number
name: string
}
export interface User {
id: number
name: string
}
// 文件:api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: '/',
}),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => '/posts',
providesTags: (result, error, arg) =>
result
? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']
: ['Post'],
}),
getUsers: build.query<User[], void>({
query: () => '/users',
providesTags: ['User'],
}),
addPost: build.mutation<Post, Omit<Post, 'id'>>({
query: (body) => ({
url: 'post',
method: 'POST',
body,
}),
invalidatesTags: ['Post'],
}),
editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
invalidatesTags: (result, error, arg) => [{ type: 'Post', id: arg.id }],
}),
}),
})
对于上面的示例,而不是使任何类型为 'Post'
的标签无效,调用 editPost
变更函数现在只会使提供的 id
的标签无效。即,如果端点的缓存数据没有为同一 id
提供 'Post'
,它将仍被视为 '有效',并且不会被触发自动重新获取。
为了更强地控制无效化适当的数据,你可以为给定的标签使用一个任意的 ID,如 'LIST'
。请参阅 使用抽象标签 ID 的高级无效化 以获取更多详情。
标签失效行为
下面的矩阵展示了哪些失效的标签会影响和使哪些提供的标签失效的例子:
提供的 失效的 | 通用标签 A ['Post'] / [{ type: 'Post' }] | 通用标签 B ['User'] / [{ type: 'User' }] | 特定标签 A1 [{ type: 'Post', id: 1 }] | 特定标签 A2 [{ type: 'Post', id: 'LIST' }] | 特定标签 B1 [{ type: 'User', id: 1 }] | 特定标签 B2 [{ type: 'User', id: 2 }] |
---|---|---|---|---|---|---|
通用标签 A ['Post'] / [{ type: 'Post' }] | ✔️ | ✔️ | ✔️ | |||
通用标签 B ['User'] / [{ type: 'User' }] | ✔️ | ✔️ | ✔️ | |||
特定标签 A1 [{ type: 'Post', id: 1 }] | ✔️ | |||||
特定标签 A2 [{ type: 'Post', id: 'LIST' }] | ✔️ | |||||
特定标签 B1 [{ type: 'User', id: 1 }] | ✔️ | |||||
特定标签 B2 [{ type: 'User', id: 2 }] | ✔️ |
以下部分根据标签的特异性总结了失效行为。
通用标签
例如,['Post'] / [{ type: 'Post' }]
将使任何具有匹配类型的提供
的标签失效
,包括通用和特定标签。
示例:
如果一个Post
的通用标签被使无效,那么提供
以下标签的数据的端点都会使其数据无效:
['Post']
[{ type: 'Post' }]
[{ type: 'Post' }, { type: 'Post', id: 1 }]
[{ type: 'Post', id: 1 }]
[{ type: 'Post', id: 1 }, { type: 'User' }]
[{ type: 'Post', id: 'LIST' }]
[{ type: 'Post', id: 1 }, { type: 'Post', id: 'LIST' }]
提供以下标签的数据的端点,其数据将_不会_被使无效:
['User']
[{ type: 'User' }]
[{ type: 'User', id: 1 }]
[{ type: 'User', id: 'LIST' }]
[{ type: 'User', id: 1 }, { type: 'User', id: 'LIST' }]
特定标签
例如,[{ type: 'Post', id: 1 }]
将使任何具有匹配类型和匹配ID的provided
标签失效
。不会直接导致general
标签失效,但如果它也提供了匹配的specific
标签,_可能_会使提供general
标签的端点的数据失效。
示例1:
如果一个特定标签{ type: 'Post', id: 1 }
被使无效,那么数据provided
以下标签的端点都会使其数据失效:
[{ type: 'Post' }, { type: 'Post', id: 1 }]
[{ type: 'Post', id: 1 }]
[{ type: 'Post', id: 1 }, { type: 'User' }]
[{ type: 'Post', id: 1 }, { type: 'Post', id: 'LIST' }]
提供以下标签数据的端点,其数据将_不会_被无效化:
['Post']
[{ type: 'Post' }]
[{ type: 'Post', id: 'LIST' }]
['User']
[{ type: 'User' }]
[{ type: 'User', id: 1 }]
[{ type: 'User', id: 'LIST' }]
[{ type: 'User', id: 1 }, { type: 'User', id: 'LIST' }]
示例 2:
如果 { type: 'Post', id: 'LIST' }
的特定标签被无效化,那么提供
以下标签数据的所有端点都会被无效化:
[{ type: 'Post', id: 'LIST' }]
[{ type: 'Post', id: 1 }, { type: 'Post', id: 'LIST' }]
提供
以下标签数据的端点,其数据将_不会_被无效化:
['Post']
[{ type: 'Post' }]
[{ type: 'Post' }, { type: 'Post', id: 1 }]
[{ type: 'Post', id: 1 }]
[{ type: 'Post', id: 1 }, { type: 'User' }]
['User']
[{ type: 'User' }]
[{ type: 'User', id: 1 }]
[{ type: 'User', id: 'LIST' }]
[{ type: 'User', id: 1 }, { type: 'User', id: 'LIST' }]
食谱
使用抽象标签 ID 进行高级失效处理
虽然使用'实体 ID'作为标签 id
是一个常见的用例,但 id
属性并不仅限于数据库 ID。id
只是一种标记特定 标签类型
的数据子集的方式。
一个强大的用例是使用像 'LIST'
这样的 ID 作为批量查询提供的数据的标签,以及 使用实体 ID 作为单个项目的标签。这样做可以让未来的 mutations
声明它们是否使包含特定项目的数据失效(例如 { type: 'Post', id: 5 }
),或者如果它是一个 'LIST'
,则使数据失效(例如 { type: 'Post', id: 'LIST' }
)。
LIST
是一个任意字符串 - 严格来说,你可以在这里使用任何你想要的东西,比如ALL
或*
。选择自定义 id 时的重要事项是确保它不可能与查询结果返回的 id 冲突。如果你的查询结果中有未知的 id,并且你不想冒险,你可以选择下面的第3点。- 你可以添加 许多 标签类 型以获得更多的控制
[{ type: 'Posts', id: 'LIST' }, { type: 'Posts', id: 'SVELTE_POSTS' }, { type: 'Posts', id: 'REACT_POSTS' }]
- 如果使用像 'LIST' 这样的
id
的概念对你来说感觉奇怪,你可以总是添加另一个tagType
并使其根失效,但我们推荐使用如上所示的id
方法。
我们可以比较下面的场景,看看如何利用 'LIST'
id 来优化行为。
使一个类型的所有内容失效
// 文件: types.ts noEmit
export interface Post {
id: number
name: string
}
export interface User {
id: number
name: string
}
// 文件: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Post, User } from './types'
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => 'posts',
providesTags: (result) =>
result ? result.map(({ id }) => ({ type: 'Posts', id })) : ['Posts'],
}),
addPost: build.mutation<Post, Partial<Post>>({
query: (body) => ({
url: `post`,
method: 'POST',
body,
}),
invalidatesTags: ['Posts'],
}),
getPost: build.query<Post, number>({
query: (id) => `post/${id}`,
providesTags: (result, error, id) => [{ type: 'Posts', id }],
}),
}),
})
export const { useGetPostsQuery, useGetPostQuery, useAddPostMutation } = api
function App() {
const { data: posts } = useGetPostsQuery()
const [addPost] = useAddPostMutation()
return (
<div>
<AddPost onAdd={addPost} />
<PostsList />
{/* 假设每个 PostDetail 都通过 `const {data} = useGetPostQuery(id)` 订阅 */}
<PostDetail id={1} />
<PostDetail id={2} />
<PostDetail id={3} />
</div>
)
}
期望的结果
当触发 addPost
时,它会使每个 PostDetail
组件回到 isFetching
状态,因为 addPost
使根标签失效,这导致提供 'Posts' 的 每个查询 都被重新运行。在大多数情况下,这可能不是你想做的。想象一下,如果你的屏幕上有100篇帖子,它们都订阅了一个 getPost
查询 - 在这种情况下,你会创建100个请求,并向你的服务器发送大量不必要的流量,这是我们首先试图避免的!尽管用户仍然会看到最后一个好的缓存结果,并可能没有注意到除了他们的浏览器出现故障之外的任何事情,你仍然想要避免这种情况。
选择性地使列表失效
// 文件: types.ts noEmit
export interface Post {
id: number
name: string
}
export interface User {
id: number
name: string
}
// 文件: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Post, User } from './types'
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => 'posts',
providesTags: (result) =>
result
? [
...result.map(({ id }) => ({ type: 'Posts' as const, id })),
{ type: 'Posts', id: 'LIST' },
]
: [{ type: 'Posts', id: 'LIST' }],
}),
addPost: build.mutation<Post, Partial<Post>>({
query(body) {
return {
url: `post`,
method: 'POST',
body,
}
},
invalidatesTags: [{ type: 'Posts', id: 'LIST' }],
}),
getPost: build.query<Post, number>({
query: (id) => `post/${id}`,
providesTags: (result, error, id) => [{ type: 'Posts', id }],
}),
}),
})
export const { useGetPostsQuery, useAddPostMutation, useGetPostQuery } = api
function App() {
const { data: posts } = useGetPostsQuery()
const [addPost] = useAddPostMutation()
return (
<div>
<AddPost onAdd={addPost} />
<PostsList />
{/* 假设每个 PostDetail 都通过 `const {data} = useGetPostQuery(id)` 订阅 */}
<PostDetail id={1} />
<PostDetail id={2} />
<PostDetail id={3} />
</div>
)
}
期望结果
当触发 addPost
时,它只会使 PostsList
进入 isFetching
状态,因为 addPost
只使 'LIST'
id 失效,这导致 getPosts
重新运行(因为它提供了特定的 id)。所以在你的网络标签页中,你只会看到一个新的 GET /posts
请求被触发。由于单个的 getPost
查询没有被使无效,所以它们不会因 addPost
的结果而重新运行。
如果你希望 addPost
变异能刷新所有包括单个 PostDetail
组件的帖子,同时仍然只发出一个新的 GET /posts
请求,这可以通过使用 selectFromResult
选择部分数据来实现。
向缓存提供错误信息
提供给缓存的信息不仅限于成功获取的数据。这个概念可以用来告知 RTK Query,当遇到特定的失败时,为失败的缓存数据 provide
一个特定的 tag
。然后,另一个端点可以 invalidate
那个 tag
的数据,告诉 RTK Query 如果一个组件仍然订阅了失败的数据,那么重新尝试之前失败的端点。
下面的例子展示了以下行为:
- 如果查询失败并返回错误代码
401 UNAUTHORIZED
,则提供一个UNAUTHORIZED
缓存标签 - 如果查询失败并返回其他错误,则提供一个
UNKNOWN_ERROR
缓存标签 - 启用一个 'login' 变异,当 成功 时,将
invalidate
带有UNAUTHORIZED
标签的数据。 这将触发postById
端点重新触发,如果:postById
的最后一次调用遇到了未授权的错误,且- 一个组件仍然订阅了缓存的数据
- 启用一个 'refetchErroredQueries' 变异,当 调用 时,将
invalidate
带有UNKNOWN_ERROR
标签的数据。 这将触发postById
端点重新触发,如果:postById
的最后一次调用遇到了未知的错误,且- 一个组件仍然订阅了缓存的数据
// 文件: types.ts noEmit
export interface Post {
id: number
name: string
}
export interface LoginResponse {}
// 文件: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import { Post, LoginResponse } from './types'
const api = createApi({
baseQuery
:
fetchBaseQuery({ baseUrl: 'https://example.com' }),
tagTypes: ['Post', 'UNAUTHORIZED', 'UNKNOWN_ERROR'],
endpoints: (build) => ({
postById: build.query<Post, number>({
query: (id) => `post/${id}`,
providesTags: (result, error, id) =>
result
? [{ type: 'Post', id }]
: error?.status === 401
? ['UNAUTHORIZED']
: ['UNKNOWN_ERROR'],
}),
login: build.mutation<LoginResponse, void>({
query: () => '/login',
// 登录成功后,将重新获取所有当前
// 'UNAUTHORIZED' 的查询
invalidatesTags: (result) => (result ? ['UNAUTHORIZED'] : []),
}),
refetchErroredQueries: build.mutation<null, void>({
queryFn: () => ({ data: null }),
invalidatesTags: ['UNKNOWN_ERROR'],
}),
}),
})
提供/使无效常用用法的抽象
为给定的 API 切片编写 provide
和 invalidate
标签的代码将取决于多个因素,包括:
- 你的后端返回的数据的形状
- 你期望给定查询端点提供哪些标签
- 你期望给定变异端点使哪些标签无效
- 你希望使用无效化功能的程度
在声明你的 API 切片时,你可能会觉得你在复制你的代码。例如,对于两个分别提供特定实体列表的不同端点,providesTags
声明可能只在提供的 tagType
上有所不同。
例如:
// 文件: types.ts noEmit
export interface Post {
id: number
name: string
}
export interface User {
id: number
name: string
}
// 文件: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => `posts`,
providesTags: (result) =>
result
? [
{ type: 'Post', id: 'LIST' },
...result.map(({ id }) => ({ type: 'Post' as const, id })),
]
: [{ type: 'Post', id: 'LIST' }],
}),
getUsers: build.query<User[], void>({
query: () => `users`,
providesTags: (result) =>
result
? [
{ type: 'User', id: 'LIST' },
...result.map(({ id }) => ({ type: 'User' as const, id })),
]
: [{ type: 'User', id: 'LIST' }],
}),
}),
})
你可能会发现定义专为你的特定 api 设计的辅助函数来减少这种在端点定义中的样板代码是有益的。
// 文件: types.ts noEmit
export interface Post {
id: number
name: string
}
export interface User {
id: number
name: string
}
// 文件: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'
function providesList<R extends { id: string | number }[], T extends string>(
resultsWithIds: R | undefined,
tagType: T,
) {
return resultsWithIds
? [
{ type: tagType, id: 'LIST' },
...resultsWithIds.map(({ id }) => ({ type: tagType, id })),
]
: [{ type: tagType, id: 'LIST' }]
}
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query({
query: () => `posts`,
providesTags: (result) => providesList(result, 'Post'),
}),
getUsers: build.query({
query: () => `users`,
providesTags: (result) => providesList(result, 'User'),
}),
}),
})
以下是各种抽象的示例,这些抽象是为常见的REST数据格式设计的,用于提供/使标签无效,包括TypeScript支持,并考虑到'LIST'样式的高级标签无效化和'error'样式的标签无效化:RTK Query缓存工具。