Usage > Automated Refetching: cache invalidation management">Usage > Automated Refetching: cache invalidation management">
跳至主要内容

自动重新获取

如在 默认缓存行为 中所述,当为查询端点添加订阅时,只有在缓存数据不存在的情况下才会发送请求。如果存在,则会提供现有数据。

RTK Query 使用“缓存标签”系统来自动重新获取受变异端点影响的数据的查询端点。这使得您可以设计 API,以便触发特定变异会导致某个查询端点认为其缓存数据无效,并在存在活动订阅的情况下重新获取数据。

每个端点 + 参数组合都会贡献其自己的 queryCacheKey。缓存标签系统使 RTK Query 能够告知特定查询缓存已提供哪些标签。如果触发一个被认为会失效查询缓存已提供的标签的变异,则缓存数据将被视为失效,如果存在对缓存数据的活动订阅,则会重新获取数据。

有关通过其他方式触发重新获取的信息,请参阅 操作缓存行为

定义

标签

另请参阅:tagTypes API 参考

对于 RTK Query,标签只是您可以为特定数据集合提供的名称,以控制缓存和失效行为,以便重新获取数据。它可以被视为附加到缓存数据的“标签”,在变异后读取,以决定数据是否应该受到变异的影响。

标签在定义 API 时在 tagTypes 参数中定义。例如,在一个同时包含 PostsUsers 的应用程序中,您可以在调用 createApi 时定义 tagTypes: ['Post', 'User']

单个 tag 具有一个 type,表示为 string 名称,以及一个可选的 id,表示为 stringnumber。它可以表示为一个简单的字符串(例如 'Post'),也可以表示为一个形状为 {type: string, id?: string|number} 的对象(例如 [{type: 'Post', id: 1}])。

提供标签

另请参阅:providesTags API 参考

一个查询可以使其缓存的数据提供标签。这样做决定了哪个“标签”附加到查询返回的缓存数据。

providesTags 参数可以是 string 数组(例如 ['Post'])、{type: string, id?: string|number}(例如 [{type: 'Post', id: 1}])或返回此类数组的回调函数。该函数将以结果作为第一个参数、响应错误作为第二个参数以及最初传递给 query 方法的参数作为第三个参数。请注意,根据查询是否成功,结果或错误参数可能未定义。

失效标签

另请参阅:invalidatesTags API 参考

一个变异可以根据标签失效特定缓存数据。这样做决定了哪些缓存数据将被重新获取或从缓存中删除。

invalidatesTags 参数可以是 string 数组(例如 ['Post'])、{type: string, id?: string|number}(例如 [{type: 'Post', id: 1}]),或者是一个返回此类数组的回调函数。该函数将以结果作为第一个参数、响应错误作为第二个参数,以及最初传递给 query 方法的参数作为第三个参数。请注意,结果或错误参数可能根据突变是否成功而被定义为未定义。

缓存标签

RTK Query 使用“标签”的概念来确定一个端点的突变是否意图失效来自另一个端点的查询提供的某些数据。

如果缓存数据被失效,它将要么重新获取提供查询(如果组件仍在使用该数据),要么从缓存中删除数据。

在定义 API 切片时,createApi 接受一个标签类型名称数组作为 tagTypes 属性,这是一个 API 切片查询可能提供的可能标签名称选项列表。

下面的示例声明端点可能向缓存提供“帖子”和/或“用户”。

声明缓存标签的示例
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,
}),
}),
}),
})

通过将这些标签声明为可能提供给缓存的内容,它使单个突变端点能够控制它们是否影响缓存的特定部分,以及与单个端点上的 providesTagsinvalidatesTags 结合使用。

提供缓存数据

每个单独的 query 端点都可以让其缓存数据提供特定的标签。这样做可以在一个或多个查询端点的缓存数据和一个或多个突变端点的行为之间建立关系。

query 端点上的 providesTags 属性用于此目的。

信息

提供的标签在不同的 query 端点之间没有内在关系。提供的标签用于确定由端点返回的缓存数据是否应该被 失效,以及是重新获取还是从缓存中删除。如果两个单独的端点提供相同的标签,它们仍然会贡献它们自己不同的缓存数据,这些数据后来都可能被来自突变的单个标签失效。

下面的示例声明 getPosts query 端点使用 query 端点的 providesTags 属性向缓存 提供 'Post' 标签。

向缓存提供标签的示例
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 相关联,这些 ID 由端点返回的结果确定

向缓存提供带有 ID 的标签的示例
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 的高级失效

失效缓存数据

每个单独的变异端点都可以invalidate现有缓存数据的特定标签。这样做使一个或多个查询端点的缓存数据与一个或多个变异端点的行为之间建立关系。

变异端点上的invalidatesTags属性用于此目的。

下面的示例声明,addPosteditPost变异端点invalidate任何带有'Post'标签的缓存数据,使用变异端点的invalidatesTags属性

失效缓存中标签的示例
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'标签的缓存数据,它将自动重新获取以从服务器检索最新数据。

一个示例场景如下所示

  1. 渲染一个组件,该组件使用useGetPostsQuery()钩子订阅该端点的缓存数据
  2. /posts请求被发出,服务器响应带有 ID 为 1、2 和 3 的帖子
  3. getPosts 端点将接收到的数据存储在缓存中,并在内部注册已提供的以下标签
    [
    { type: 'Post', id: 1 },
    { type: 'Post', id: 2 },
    { type: 'Post', id: 3 },
    ]
  4. editPost 变异被触发以更改特定帖子
  5. 完成后,RTK Query 在内部注册 'Post' 标签现在已失效,并从缓存中删除先前提供的 'Post' 标签
  6. 由于 getPosts 端点已提供类型为 'Post' 的标签,而该标签现在具有无效的缓存数据,并且组件仍然订阅了该数据,因此 /posts 请求会自动再次触发,获取新数据并为更新的缓存数据注册新标签

为了更精细地控制失效数据,失效的 tags 可以与 providesTags 相同的方式关联一个 id。这使得能够区分“特定标签类型的任何一个”和“特定标签类型的特定实例”。

以下示例声明 editPost 变异使 Post 标签的特定实例失效,使用调用变异函数时传入的 ID

使用 ID 使缓存中的标签失效的示例
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

为了更强地控制失效适当的数据,您可以为给定标签使用任意 ID,例如'LIST'。有关更多详细信息,请参阅使用抽象标签 ID 的高级失效

标签失效行为

下面的矩阵显示了哪些失效的标签会影响和使哪些提供的标签失效的示例

已提供
已失效
通用标签 A
['Post']
/
[{ type: 'Post' }]
通用标签 B
['User']
/
[{ type: 'User' }]
特定标签 A1
[{ 类型: 'Post',
id: 1 }]
特定标签 A2
[{ 类型: 'Post', id: 'LIST' }]
特定标签 B1
[{ 类型: 'User',
id: 1 }]
特定标签 B2
[{ 类型: 'User',
id: 2 }]
通用标签 A
['Post'] / [{ 类型: 'Post' }]
✔️✔️✔️
通用标签 B
['User'] /
[{ type: 'User' }]
✔️✔️✔️
特定标签 A1
[{ 类型: 'Post', id: 1 }]
✔️
特定标签 A2
[{ 类型: 'Post', id: 'LIST' }]
✔️
特定标签 B1
[{ 类型: 'User', id: 1 }]
✔️
特定标签 B2
[{ 类型: 'User', id: 2 }]
✔️

以下部分根据标签的具体性总结了失效行为。

通用标签

例如:['Post'] / [{ 类型: 'Post' }]

失效任何提供与匹配类型匹配的标签,包括通用标签和特定标签。

示例
如果Post的通用标签失效,则数据提供以下标签的端点将全部失效

  • ['Post']
  • [{ type: 'Post' }]
  • [{ 类型: 'Post' }, { 类型: 'Post', id: 1 }]
  • [{ 类型: 'Post', id: 1 }]
  • [{ 类型: 'Post', id: 1 }, { 类型: 'User' }]
  • [{ 类型: 'Post', id: 'LIST' }]
  • [{ 类型: 'Post', id: 1 }, { 类型: 'Post', id: 'LIST' }]

数据提供以下标签的端点将不会失效

  • ['User']
  • [{ type: 'User' }]
  • [{ 类型: 'User', id: 1 }]
  • [{ 类型: 'User', id: 'LIST' }]
  • [{ 类型: 'User', id: 1 }, { 类型: 'User', id: 'LIST' }]

特定标签

例如:[{ 类型: 'Post', id: 1 }]

失效任何提供与匹配类型匹配 ID 匹配的标签。不会直接导致通用标签失效,但可能使提供通用标签的端点的失效,如果它也提供匹配的特定标签。

示例 1:如果{ 类型: 'Post', id: 1 }的特定标签失效,则数据提供以下标签的端点将全部失效

  • [{ 类型: 'Post' }, { 类型: 'Post', id: 1 }]
  • [{ 类型: 'Post', id: 1 }]
  • [{ 类型: 'Post', id: 1 }, { 类型: 'User' }]
  • [{ 类型: 'Post', id: 1 }, { 类型: 'Post', id: 'LIST' }]

数据提供以下标签的端点将不会失效

  • ['Post']
  • [{ type: 'Post' }]
  • [{ 类型: 'Post', id: 'LIST' }]
  • ['User']
  • [{ type: 'User' }]
  • [{ 类型: 'User', id: 1 }]
  • [{ 类型: 'User', id: 'LIST' }]
  • [{ 类型: 'User', id: 1 }, { 类型: 'User', id: 'LIST' }]

示例 2:如果{ 类型: 'Post', id: 'LIST' }的特定标签失效,则数据提供以下标签的端点将全部失效

  • [{ 类型: 'Post', id: 'LIST' }]
  • [{ 类型: 'Post', id: 1 }, { 类型: 'Post', id: 'LIST' }]

数据提供以下标签的端点将不会失效

  • ['Post']
  • [{ type: 'Post' }]
  • [{ 类型: 'Post' }, { 类型: 'Post', id: 1 }]
  • [{ 类型: 'Post', id: 1 }]
  • [{ 类型: 'Post', id: 1 }, { 类型: 'User' }]
  • ['User']
  • [{ type: 'User' }]
  • [{ 类型: 'User', id: 1 }]
  • [{ 类型: 'User', id: 'LIST' }]
  • [{ 类型: 'User', id: 1 }, { 类型: 'User', id: 'LIST' }]

食谱

使用抽象标签 ID 的高级失效

虽然使用“实体 ID”作为标签id是一个常见用例,但id属性并不局限于仅使用数据库 ID。id仅仅是为特定标签类型的特定数据集合标记子集的一种方式。

一个强大的用例是使用像 'LIST' 这样的 ID 作为批量查询提供的数据的标签,以及使用实体 ID 来标识单个项目。这样做允许未来的 mutations 声明它们是否仅在数据包含特定项目(例如 { type: 'Post', id: 5 })时使数据失效,或者如果数据是 'LIST'(例如 { type: 'Post', id: 'LIST' })时使数据失效。

'LIST' 标签和 ID
  1. LIST 是一个任意的字符串 - 从技术上讲,你可以在这里使用任何你想要的东西,比如 ALL*。选择自定义 ID 时,重要的是要确保它不可能与查询结果返回的 ID 冲突。如果你在查询结果中存在未知的 ID,并且不想冒险,你可以选择下面的第 3 点。
  2. 你可以添加多个标签类型以获得更多控制
    • [{ type: 'Posts', id: 'LIST' }, { type: 'Posts', id: 'SVELTE_POSTS' }, { type: 'Posts', id: 'REACT_POSTS' }]
  3. 如果你觉得使用像 'LIST' 这样的 id 感觉很奇怪,你总是可以添加另一个 tagType 并使它的根失效,但我们建议使用如上所示的 id 方法。

我们可以比较下面的场景,看看如何使用 'LIST' ID 来优化行为。

使所有类型的数据失效

API 定义
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
App.tsx
function App() {
const { data: posts } = useGetPostsQuery()
const [addPost] = useAddPostMutation()

return (
<div>
<AddPost onAdd={addPost} />
<PostsList />
{/* Assume each PostDetail is subscribed via `const {data} = useGetPostQuery(id)` */}
<PostDetail id={1} />
<PostDetail id={2} />
<PostDetail id={3} />
</div>
)
}

预期结果

addPost 被触发时,它会导致每个 PostDetail 组件回到 isFetching 状态,因为 addPost 使根标签失效,这会导致所有提供 'Posts' 的查询重新运行。在大多数情况下,这可能不是你想要的。想象一下,如果你在屏幕上有 100 个帖子,它们都订阅了 getPost 查询 - 在这种情况下,你会创建 100 个请求,并向你的服务器发送大量不必要的流量,而这正是我们想要避免的!即使用户仍然可以看到最后一次缓存的良好结果,并且可能不会注意到除了浏览器卡顿之外的任何其他问题,你仍然想要避免这种情况。

有选择地使列表失效

API 定义
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
App.tsx
function App() {
const { data: posts } = useGetPostsQuery()
const [addPost] = useAddPostMutation()

return (
<div>
<AddPost onAdd={addPost} />
<PostsList />
{/* Assume each PostDetail is subscribed via `const {data} = useGetPostQuery(id)` */}
<PostDetail id={1} />
<PostDetail id={2} />
<PostDetail id={3} />
</div>
)
}

预期结果

addPost 被触发时,它只会导致 PostsList 进入 isFetching 状态,因为 addPost 只使 'LIST' ID 失效,这会导致 getPosts 重新运行(因为它提供了该特定 ID)。因此,在你的网络标签中,你只会看到 1 个新的 GET /posts 请求。由于单个 getPost 查询没有被使失效,因此它们不会因为 addPost 而重新运行。

信息

如果您希望addPost 变异刷新所有帖子,包括单个PostDetail 组件,同时只发出 1 个新的GET /posts 请求,可以使用 selectFromResult 从查询结果中选择数据。

向缓存提供错误

提供给缓存的信息不仅限于成功的数据获取。该概念可用于告知 RTK Query,当遇到特定失败时,应为该失败的缓存数据提供特定标签。然后,另一个端点可以失效标签的数据,告诉 RTK Query 如果组件仍然订阅了失败的数据,则重新尝试之前失败的端点。

以下示例演示了具有以下行为的示例

  • 如果查询失败并出现错误代码401 UNAUTHORIZED,则提供UNAUTHORIZED 缓存标签
  • 如果查询失败并出现其他错误,则提供UNKNOWN_ERROR 缓存标签
  • 启用“登录”变异,该变异成功时将失效带有UNAUTHORIZED 标签的数据。
    这将触发postById 端点重新触发,如果
    1. postById 的最后一次调用遇到了未授权错误,并且
    2. 组件仍然订阅了缓存数据
  • 启用“重新获取错误查询”变异,该变异调用时将失效带有UNKNOWN_ERROR 标签的数据。
    这将触发postById 端点重新触发,如果
    1. postById 的最后一次调用遇到了未知错误,并且
    2. 组件仍然订阅了缓存数据
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',
// on successful login, will refetch all currently
// 'UNAUTHORIZED' queries
invalidatesTags: (result) => (result ? ['UNAUTHORIZED'] : []),
}),
refetchErroredQueries: build.mutation<null, void>({
queryFn: () => ({ data: null }),
invalidatesTags: ['UNKNOWN_ERROR'],
}),
}),
})

抽象化常见的提供/失效用法

为给定 API 片段编写提供失效标签的代码将取决于多个因素,包括

  • 后端返回的数据形状
  • 您期望给定查询端点提供的标签
  • 您期望给定变异端点失效的标签
  • 您希望使用失效功能的程度

在声明 API 片段时,您可能会觉得代码重复。例如,对于两个都提供特定实体列表的独立端点,providesTags 声明可能只在提供的tagType 中有所不同。

例如

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 设计的辅助函数有利于减少端点定义中的这种样板代码,例如

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'),
}),
}),
})

以下 gist 中展示了为常见的 rest 数据格式设计的各种标签提供/失效抽象示例,包括 typescript 支持,以及对 'LIST' 风格高级标签失效'错误' 风格标签失效 的分解:RTK Query 缓存实用程序