跳到主要内容

 

API 切片:工具函数

API 切片对象包含各种用于缓存管理的工具函数,例如实现乐观更新,以及实现服务端渲染

这些工具函数包含在 API 对象的 api.util 中。

信息

由于实际内部类型相当复杂,此页面上的一些 TS 类型是用伪代码表示以说明用途。

updateQueryData

一个 Redux thunk action creator,当被分发时,会创建并应用一组 JSON diff/patch 对象到当前状态。这会立即使用这些更改更新 Redux 状态。

签名

const updateQueryData = (
endpointName: string,
arg: any,
updateRecipe: (draft: Draft<CachedState>) => void,
updateProvided?: boolean,
) => ThunkAction<PatchCollection, PartialState, any, AnyAction>

interface PatchCollection {
patches: Patch[]
inversePatches: Patch[]
undo: () => void
}

参数

  • endpointName: 匹配现有端点名称的字符串
  • arg: 与之前查询调用使用的参数匹配的参数,用于确定需要更新哪个缓存数据集
  • updateRecipe: 一个 Immer produce 回调函数,可以对缓存状态进行修改
  • updateProvided: 一个布尔值,指示是否应该基于更新后的缓存重新计算端点的提供的标签。默认为 false

描述

该 thunk action creator 接受三个参数:要更新的端点名称(如 'getPost'),任何相关的查询参数,以及一个回调函数。该回调接收当前状态的 Immer 包装的 draft,并可以修改草稿以匹配突变成功完成后的预期结果。

thunk 返回一个包含 {patches: Patch[], inversePatches: Patch[], undo: () => void} 的对象。patchesinversePatches 是使用 Immer 的 produceWithPatches 方法生成的。

这通常用作实现乐观更新的第一步。通过调用 dispatch(patchQueryData(endpointName, arg, inversePatches)) 可以使用生成的 inversePatches 来撤销更新。或者,可以直接调用 undo 方法来达到相同的效果。

注意前两个参数(endpointNamearg)用于确定要更新哪个现有的缓存条目。如果找不到现有的缓存条目,updateRecipe 回调将不会运行。

示例 1

const patchCollection = dispatch(
api.util.updateQueryData('getPosts', undefined, (draftPosts) => {
draftPosts.push({ id: 1, name: 'Teddy' })
}),
)

在上面的示例中,'getPosts' 作为 endpointName 提供,undefined 作为 arg 提供。这将匹配缓存键为 'getPosts(undefined)' 的查询。

即,它将匹配可能通过以下任何调用创建的缓存条目:

api.endpoints.getPosts.useQuery()

useGetPostsQuery()

useGetPostsQuery(undefined, { ...options })

dispatch(api.endpoints.getPosts.initiate())

dispatch(api.endpoints.getPosts.initiate(undefined, { ...options }))

示例 2

const patchCollection = dispatch(
api.util.updateQueryData('getPostById', 1, (draftPost) => {
draftPost.name = 'Lilly'
}),
)

在上面的示例中,'getPostById' 作为 endpointName 提供,1 作为 arg 提供。这将匹配缓存键为 'getPostById(1)' 的查询。

即,它将匹配可能通过以下任何调用创建的缓存条目:

api.endpoints.getPostById.useQuery(1)

useGetPostByIdQuery(1)

useGetPostByIdQuery(1, { ...options })

dispatch(api.endpoints.getPostById.initiate(1))

dispatch(api.endpoints.getPostById.initiate(1, { ...options }))

upsertQueryData

一个 Redux thunk action creator,当被分发时,会作为一个人工 API 请求将值插入到缓存中。

签名

const upsertQueryData = <T>(endpointName: string, arg: any, newEntryData: T) =>
ThunkAction<Promise<CacheEntry<T>>, PartialState, any, UnknownAction>

参数

  • endpointName: 匹配现有端点名称的字符串
  • arg: 与之前查询调用使用的参数匹配的参数,用于确定需要更新哪个缓存数据集
  • newEntryValue: 要写入相应缓存条目的 data 字段的值

描述

该 thunk action creator 接受三个参数:要更新的端点名称(如 'getPost'),适当的查询参数值以构造所需的缓存键,以及要插入的数据。

如果该缓存键不存在缓存条目,将创建一个缓存条目并添加数据。如果已存在缓存条目,这将_覆盖_现有的缓存条目数据。

thunk 异步执行,并返回一个 promise,该 promise 在存储更新完成时解析。这包括执行为该端点定义的 transformResponse 回调(如果有)。

如果在实际请求正在进行时分发,upsert 和请求都将在它们解析后立即处理,导致"最后结果获胜"的更新行为。

示例

await dispatch(
api.util.upsertQueryData('getPost', { id: 1 }, { id: 1, text: 'Hello!' }),
)

patchQueryData

一个 Redux thunk action creator,当被分发时,将 JSON diff/patch 数组应用于给定查询结果的缓存数据。这会立即使用这些更改更新 Redux 状态。

签名

const patchQueryData = (
endpointName: string,
arg: any
patches: Patch[],
updateProvided?: boolean
) => ThunkAction<void, PartialState, any, UnknownAction>;

参数

  • endpointName: 匹配现有端点名称的字符串
  • arg: 缓存键,用于确定需要更新哪个缓存数据集
  • patches: 要应用于缓存状态的patches(或inverse patches)数组。这些通常是从分发updateQueryData的结果中获得的
  • updateProvided: 布尔值,指示是否应该基于更新后的缓存重新计算端点的提供的标签。默认为false

描述

该 thunk action creator 接受三个参数:要更新的端点名称(如'getPost'),适当的查询参数值以构造所需的缓存键,以及由 Immer 的produceWithPatches生成的 JSON diff/patch 数组。

这通常用作实现乐观更新的第二步。如果请求失败,可以通过使用先前由updateQueryData生成的inversePatches分发patchQueryData来还原乐观应用的更改。

在希望简单地还原先前更改的情况下,可能更适合调用从分发updateQueryData返回的undo方法。

示例

const patchCollection = dispatch(
api.util.updateQueryData('getPosts', undefined, (draftPosts) => {
draftPosts.push({ id: 1, name: 'Teddy' })
}),
)

// 之后
dispatch(
api.util.patchQueryData(
'getPosts',
undefined,
patchCollection.inversePatches,
),
)

// 或者
patchCollection.undo()

upsertQueryEntries

一个标准的 Redux action creator,它接受一个单独缓存条目描述的数组,并立即将它们插入到存储中。这旨在有效地一次性批量插入多个条目。

签名

/**
* 一个类型安全的要插入到缓存中的单个条目
*/
export type NormalizedQueryUpsertEntry<
Definitions extends EndpointDefinitions,
EndpointName extends QueryKeys<Definitions>,
> = {
endpointName: EndpointName
arg: QueryArgFrom<Definitions[EndpointName]>
value: ResultTypeFrom<Definitions[EndpointName]>
}

const upsertQueryEntries = (entries: NormalizedQueryUpsertEntry[]) =>
PayloadAction<NormalizedQueryUpsertEntry[]>

参数

  • entries: 包含需要插入的单个缓存条目描述的数组:
    • endpointName: 端点的名称,例如 "getPokemon"
    • arg: 用于标识此缓存条目的完整查询键参数,例如 "pikachu"(与传递给 useQuery 钩子或 api.endpoints.someEndpoint.select() 的参数相同)
    • value: 要插入到此缓存条目的数据,格式完全相同。

描述

此方法设计为比多次单独调用 upsertQueryData 更有效的批量插入多个条目的方法。作为比较:

  • upsertQueryData:
    • 一次插入一个缓存条目
    • 是异步的
    • 分发 2 个单独的操作,pendingfulfilled
    • 如果为该端点定义了 transformResponse 回调,则运行该回调,以及如果定义了 merge 回调,则运行该回调
  • upsertQueryEntries:
    • 一次插入多个缓存条目,它们可以是 API 中定义的任何端点的组合
    • 是一个单一的同步操作
    • 不运行 transformResponse,因此提供的 value 字段必须已经是该端点预期的最终格式。然而,如果定义了 merge 回调,它仍然会运行

目前,此方法有两个主要用例。第一个是在应用程序启动时使用从存储中检索的数据预填充缓存。第二个是作为"伪规范化"工具。RTK Query 不是"规范化"缓存。然而,有时您可能希望使用另一个端点的内容预填充其他缓存条目,例如获取 getPosts 列表端点响应的结果并预填充单个 getPost(id) 端点缓存条目。

如果该缓存键不存在缓存条目,将创建一个缓存条目并添加数据。如果已存在缓存条目,这将_覆盖_现有的缓存条目数据。

如果在实际请求正在进行时分发,upsert 和请求都将在它们解析后立即处理,导致"最后结果获胜"的更新行为。

示例

const api = createApi({
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => '/posts',
async onQueryStarted(_, { dispatch, queryFulfilled }) {
const res = await queryFulfilled
const posts = res.data

// 使用列表端点查询的结果预填充单个帖子条目
dispatch(
api.util.upsertQueryEntries(
posts.map((post) => ({
endpointName: 'getPost',
arg: { id: post.id },
value: post,
})),
),
)
},
}),
getPost: build.query<Post, Pick<Post, 'id'>>({
query: (post) => `post/${post.id}`,
}),
}),
})

prefetch

一个 Redux thunk action creator,可用于手动触发数据预取。

签名

type PrefetchOptions = { ifOlderThan?: false | number } | { force?: boolean }

const prefetch = (endpointName: string, arg: any, options: PrefetchOptions) =>
ThunkAction<void, any, any, UnknownAction>

参数

  • endpointName: 匹配现有端点名称的字符串
  • args: 缓存键,用于确定需要更新哪个缓存数据集
  • options: 用于确定在给定情况下是否应发送请求的选项:
    • ifOlderThan: 如果指定,仅在 new Date() 和最后 fulfilledTimeStamp 之间的差异大于给定值(以秒为单位)时运行查询
    • force: 如果为 true,则会忽略 ifOlderThan 值(如果已设置),并且即使它存在于缓存中,查询也会运行。

描述

该 thunk action creator 接受三个参数:要更新的端点名称(如 'getPost'),任何相关的查询参数,以及一组用于根据缓存陈旧性确定是否实际应重新获取数据的选项。

React Hooks 用户很可能永远不需要直接使用它,因为 usePrefetch 钩子将在您调用钩子提供的预取函数时内部分发 thunk action creator 结果。

示例

dispatch(api.util.prefetch('getPosts', undefined, { force: true }))

selectInvalidatedBy

一个选择器函数,可以选择要失效的查询参数。

签名

function selectInvalidatedBy(
state: RootState,
tags: ReadonlyArray<TagDescription<string>>,
): Array<{
endpointName: string
originalArgs: any
queryCacheKey: QueryCacheKey
}>

参数

  • state: 根状态
  • tags: 只读的失效标签数组,其中提供的 TagDescription 是提供给 api 的 tagTypes 属性的字符串之一。例如
    • [TagType]
    • [{ type: TagType }]
    • [{ type: TagType, id: number | string }]

描述

该函数接受两个参数

  • 根状态和
  • 要失效的缓存标签。

它返回一个包含

  • 端点名称,
  • 原始参数和
  • queryCacheKey 的数组。

示例

const entries = api.util.selectInvalidatedBy(state, ['Post'])
const entries = api.util.selectInvalidatedBy(state, [{ type: 'Post', id: 1 }])
const entries = api.util.selectInvalidatedBy(state, [
{ type: 'Post', id: 1 },
{ type: 'Post', id: 4 },
])

invalidateTags

一个 Redux action creator,可用于手动使缓存标签失效以进行自动重新获取

签名

const invalidateTags = (
tags: Array<TagTypes | FullTagDescription<TagTypes>>,
) => ({
type: string,
payload: tags,
})

参数

  • tags: 要失效的标签数组,其中提供的 TagType 是提供给 api 的 tagTypes 属性的字符串之一。例如
    • [TagType]
    • [{ type: TagType }]
    • [{ type: TagType, id: number | string }]

描述

该 action creator 接受一个参数:要失效的缓存标签。它返回一个包含这些标签作为有效负载的操作,以及相应的 api 的 invalidateTags 操作类型。

分发此 action creator 结果将使给定标签失效,导致查询自动重新获取,如果它们订阅了提供相应标签的缓存数据。

示例

dispatch(api.util.invalidateTags(['Post']))
dispatch(api.util.invalidateTags([{ type: 'Post', id: 1 }]))
dispatch(
api.util.invalidateTags([
{ type: 'Post', id: 1 },
{ type: 'Post', id: 'LIST' },
]),
)

selectCachedArgsForQuery

一个选择器函数,可以选择当前缓存查询的参数。

签名

function selectCachedArgsForQuery(
state: RootState,
queryName: QueryName,
): Array<QueryArg>

参数

  • state: 根状态
  • queryName: 匹配现有查询端点名称的字符串

描述

该函数接受两个参数

  • 根状态和

  • 查询的名称

它返回一个包含每个条目使用的参数的数组。

示例

const args = api.util.selectCachedArgsForQuery(state, 'getPosts')

resetApiState

签名

const resetApiState = () => ({
type: string,
payload: undefined,
})

描述

一个 Redux action creator,可以分发以手动完全重置 api 状态。这将立即删除所有现有的缓存条目,并且所有查询将被视为"未初始化"。

请注意,钩子 还会在本地组件状态中跟踪状态,并且可能不会完全通过 resetApiState 重置。

示例

dispatch(api.util.resetApiState())

getRunningQueriesThunkgetRunningMutationsThunk

签名

getRunningQueriesThunk(): ThunkWithReturnValue<Array<QueryActionCreatorResult<any>>>
getRunningMutationsThunk(): ThunkWithReturnValue<Array<MutationActionCreatorResult<any>>>

描述

thunks(如果被分发)返回所有正在运行的查询或突变。 这些返回值可以像 promise 一样等待。

这对于 SSR 场景很有用,可以等待所有以任何方式触发的查询(或突变),包括通过钩子调用或手动分发 initiate 操作。

等待所有当前正在运行的查询示例
await Promise.all(dispatch(api.util.getRunningQueriesThunk()))

getRunningQueryThunkgetRunningMutationThunk

签名

getRunningQueryThunk<EndpointName extends QueryKeys<Definitions>>(
endpointName: EndpointName,
args: QueryArgFrom<Definitions[EndpointName]>
): ThunkWithReturnValue<
| QueryActionCreatorResult<
Definitions[EndpointName] & { type: 'query' }
>
| undefined
>

getRunningMutationThunk<EndpointName extends MutationKeys<Definitions>>(
endpointName: EndpointName,
fixedCacheKeyOrRequestId: string
): ThunkWithReturnValue<
| MutationActionCreatorResult<
Definitions[EndpointName] & { type: 'mutation' }
>
| undefined
>

描述

thunks(如果被分发)返回给定端点名称+参数(或 requestId/fixedCacheKey)组合的单个正在运行的查询(或突变),如果它当前正在运行。如果它当前未运行,该函数返回 undefined

这些 thunks 主要是为了在未来添加对 suspense 的实验性支持。 它们使编写自定义钩子成为可能,这些钩子查找 RTK Query 是否已经有一个正在运行的查询/突变,用于某个端点/参数组合,并检索它以将其作为 promise throw