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
: 一个 Immerproduce
回调函数,可以对缓存状态进行修改updateProvided
: 一个布尔值,指示是否应该基于更新后的缓存重新计算端点的提供的标签。默认为false
。
描述
该 thunk action creator 接受三个参数:要更新的端点名称(如 'getPost'
),任何相关的查询参数,以及一个回调函数。该回调接收当前状态的 Immer 包装的 draft
,并可以修改草稿以匹配突变成功完成后的预期结果。
thunk 返回一个包含 {patches: Patch[], inversePatches: Patch[], undo: () => void}
的对象。patches
和 inversePatches
是使用 Immer 的 produceWithPatches
方法生成的。
这通常用作实现乐观更新的第一步。通过调用 dispatch(patchQueryData(endpointName, arg, inversePatches))
可以使用生成的 inversePatches
来撤销更新。或者,可以直接调用 undo
方法来达到相同的效果。
注意前两个参数(endpointName
和 arg
)用于确定要更新哪个现有的缓存条目。如果找不到现有的缓存条目,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 个单独的操作,
pending
和fulfilled
- 如果为该端点定义了
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())
getRunningQueriesThunk
和 getRunningMutationsThunk
签名
getRunningQueriesThunk(): ThunkWithReturnValue<Array<QueryActionCreatorResult<any>>>
getRunningMutationsThunk(): ThunkWithReturnValue<Array<MutationActionCreatorResult<any>>>
描述
thunks(如果被分发)返回所有正在运行的查询或突变。 这些返回值可以像 promise 一样等待。
这对于 SSR 场景很有用,可以等待所有以任何方式触发的查询(或突变),包括通过钩子调用或手动分发 initiate
操作。
await Promise.all(dispatch(api.util.getRunningQueriesThunk()))
getRunningQueryThunk
和 getRunningMutationThunk
签名
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
。