createApi
createApi
是 RTK Query 功能的核心。它允许你定义一组"端点",描述如何从后端 API 和其他异步源获取数据,包括如何获取和转换这些数据的配置。它生成一个 "API 切片" 结构,该结构包含 Redux 逻辑(可选的 React 钩子),为你封装了数据获取和缓存过程。
// 文件:src/services/types.ts noEmit
export type Pokemon = {}
// 文件:src/services/pokemon.ts
// 需要使用 React-specific 入口点以生成 React 钩子
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Pokemon } from './types'
// 使用基础 URL 和预期的端点定义服务
export const pokemonApi = createApi({
reducerPath: 'pokemonApi',
baseQuery: fetch
Base
Query({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query<Pokemon, string>({
query: (name) => `pokemon/${name}`,
}),
}),
})
// 导出用于函数组件的钩子,这些钩子是
// 基于定义的端点自动生成的
export const { useGetPokemonByNameQuery } = pokemonApi
参数
createApi
接受一个包含以下选项的单一配置对象参数:
baseQuery(args: InternalQueryArgs, api: BaseQueryApi, extraOptions?: DefinitionExtraOptions): any;
endpoints(build: EndpointBuilder<InternalQueryArgs, TagTypes>): Definitions;
extractRehydrationInfo?: (
action: UnknownAction,
{
reducerPath,
}: {
reducerPath: ReducerPath
}
) =>
| undefined
| CombinedState<Definitions, TagTypes, ReducerPath>
tagTypes?: readonly TagTypes[];
reducerPath?: ReducerPath;
serializeQueryArgs?: SerializeQueryArgs<InternalQueryArgs>;
keepUnusedDataFor?: number; // 值以秒为单位
refetchOnMountOrArgChange?: boolean | number; // 值以秒为单位
refetchOnFocus?: boolean;
refetchOnReconnect?: boolean;
baseQuery
如果没有指定 queryFn
选项,每个端点使用的基础查询。RTK Query 导出了一个名为 fetchBaseQuery 的实用程序,作为 fetch
的轻量级包装,用于常见的用例。如果 fetchBaseQuery
无法满足你的需求,请参阅 自定义查询。
baseQuery 函数参数
args
- 给定端点的query
函数的返回值api
-BaseQueryApi
对象包含:signal
- 一个AbortSignal
对象,可用于中止 DOM 请求和/或读取请求是否被中止。abort
- 附加到signal
的 AbortController 的abort()
方法。dispatch
- 对应 Redux 存储的store.dispatch
方法getState
- 可调用的函数,用于访问当前存储状态extra
- 作为 thunk.extraArgument 提供给 configureStore 的 getDefaultMiddleware 选项。endpoint
- 端点的名称。type
- 请求类型(query
或mutation
)。forced
- 表示查询是否被强制。
extraOptions
- 给定端点提供的可选extraOptions
属性的值
baseQuery 函数签名
export type BaseQueryFn<
Args = any,
Result = unknown,
Error = unknown,
DefinitionExtraOptions = {},
Meta = {},
> = (
args: Args,
api: BaseQueryApi,
extraOptions: DefinitionExtraOptions,
) => MaybePromise<QueryReturnValue<Result, Error, Meta>>
export interface BaseQueryApi {
signal: AbortSignal
abort: (reason?: string) => void
dispatch: ThunkDispatch<any, any, any>
getState: () => unknown
extra: unknown
endpoint: string
type: 'query' | 'mutation'
forced?: boolean
}
export type QueryReturnValue<T = unknown, E = unknown, M = unknown> =
| {
error: E
data?: undefined
meta?: M
}
| {
error?: undefined
data: T
meta?: M
}
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
// ...端点
}),
})
endpoints
端点只是你想对服务器执行的一组操作。你使用构建器语法将它们定义为一个对象。有两种基本的端点类型:query
和 mutation
。
请参阅 端点的解剖 了解单个属性的详细信息。
查询端点定义
export type QueryDefinition<
QueryArg,
BaseQuery extends BaseQueryFn,
TagTypes extends string,
ResultType,
ReducerPath extends string = string,
> = {
query(arg: QueryArg): BaseQueryArg<BaseQuery>
/* `query` 或 `queryFn` 可以存在,但不能同时存在 */
queryFn(
arg: QueryArg,
api: BaseQueryApi,
extraOptions: BaseQueryExtraOptions<BaseQuery>,
baseQuery: (arg: Parameters<BaseQuery>[0]) => ReturnType<BaseQuery>,
): MaybePromise<QueryReturnValue<ResultType, BaseQueryError<BaseQuery>>>
/* transformResponse 只在 `query` 中可用,不在 `queryFn` 中可用 */
transformResponse?(
baseQueryReturnValue: BaseQueryResult<BaseQuery>,
meta: BaseQueryMeta<BaseQuery>,
arg: QueryArg,
): ResultType | Promise<ResultType>
/* transformErrorResponse 只在 `query` 中可用,不在 `queryFn` 中可用 */
transformErrorResponse?(
baseQueryReturnValue: BaseQueryError<BaseQuery>,
meta: BaseQueryMeta<BaseQuery>,
arg: QueryArg,
): unknown
extraOptions?: BaseQueryExtraOptions<BaseQuery>
providesTags?: ResultDescription<
TagTypes,
ResultType,
QueryArg,
BaseQueryError<BaseQuery>
>
keepUnusedDataFor?: number
onQueryStarted?(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
queryFulfilled,
getCacheEntry,
updateCachedData, // 只在查询端点可用
}: QueryLifecycleApi,
): Promise<void>
onCacheEntryAdded?(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
cacheEntryRemoved,
cacheDataLoaded,
getCacheEntry,
updateCachedData, // 只在查询端点可用
}: QueryCacheLifecycleApi,
): Promise<void>
}
变更端点定义
export type MutationDefinition<
QueryArg,
BaseQuery extends BaseQueryFn,
TagTypes extends string,
ResultType,
ReducerPath extends string = string,
Context = Record<string, any>,
> = {
query(arg: QueryArg): BaseQueryArg<BaseQuery>
/* `query` 或 `queryFn` 可以存在,但不能同时存在 */
queryFn(
arg: QueryArg,
api: BaseQueryApi,
extraOptions: BaseQueryExtraOptions<BaseQuery>,
baseQuery: (arg: Parameters<BaseQuery>[0]) => ReturnType<BaseQuery>,
): MaybePromise<QueryReturnValue<ResultType, BaseQueryError<BaseQuery>>>
/* transformResponse 只在 `query` 中可用,不在 `queryFn` 中可用 */
transformResponse?(
baseQueryReturnValue: BaseQueryResult<BaseQuery>,
meta: BaseQueryMeta<BaseQuery>,
arg: QueryArg,
): ResultType | Promise<ResultType>
/* transformErrorResponse 只在 `query` 中可用,不在 `queryFn` 中可用 */
transformErrorResponse?(
baseQueryReturnValue: BaseQueryError<BaseQuery>,
meta: BaseQueryMeta<BaseQuery>,
arg: QueryArg,
): unknown
extraOptions?: BaseQueryExtraOptions<BaseQuery>
invalidatesTags?: ResultDescription<TagTypes, ResultType, QueryArg>
onQueryStarted?(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
queryFulfilled,
getCacheEntry,
}: MutationLifecycleApi,
): Promise<void>
onCacheEntryAdded?(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
cacheEntryRemoved,
cacheDataLoaded,
getCacheEntry,
}: MutationCacheLifecycleApi,
): Promise<void>
}
如何使用端点
当定义一个像下面的 getPosts
这样的键时,需要知道这个名称将从 api
中导出,并能在 api.endpoints.getPosts.useQuery()
,api.endpoints.getPosts.initiate()
和 api.endpoints.getPosts.select()
下被引用。对于 mutation
也是一样,但它们引用的是 useMutation
而不是 useQuery
。
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => 'posts',
providesTags: (result) =>
result ? result.map(({ id }) => ({ type: 'Posts', id })) : [],
}),
addPost: build.mutation<Post, Partial<Post>>({
query: (body) => ({
url: `posts`,
method: 'POST',
body,
}),
invalidatesTags: ['Posts'],
}),
}),
})
// 自动生成的钩子
export const { useGetPostsQuery, useAddPostMutation } = api
// 可能的导出
export const { endpoints, reducerPath, reducer, middleware } = api
// reducerPath, reducer, middleware 只在存储配置中使用
// endpoints 将有:
// endpoints.getPosts.initiate(), endpoints.getPosts.select(), endpoints.getPosts.useQuery()
// endpoints.addPost.initiate(), endpoints.addPost.select(), endpoints.addPost.useMutation()
// 查看 `createApi` 概述以获取 _所有导出_
extractRehydrationInfo
传递每个调度动作的函数。如果这返回了除 undefined
以外的东西,
那么返回值将用于重新填充已完成和错误的查询。
// 代码块元数据 标题="next-redux-wrapper 重新填充示例"
import type { Action, PayloadAction } from '@reduxjs/toolkit'
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { HYDRATE } from 'next-redux-wrapper'
type RootState = any; // 通常从状态推断
function isHydrateAction(action: Action): action is PayloadAction<RootState> {
return action.type === HYDRATE
}
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
extractRehydrationInfo(action, { reducerPath }): any {
if (isHydrateAction(action)) {
return action.payload[reducerPath]
}
},
endpoints: (build) => ({
// 省略
}),
})
tagTypes
字符串标签类型名称的数组。指定标签类型是可选的,但你应该定义它们,以便它们可以用于缓存和失效。定义标签类型时,你可以在配置 endpoints 时使用 providesTags
提供 它们,并使用 invalidatesTags
使其失效。
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
// ...endpoints
}),
})
reducerPath
reducerPath
是你的服务在存储中挂载的 唯一 键。如果你在应用程序中多次调用 createApi
,每次都需要提供一个唯一的值。默认为 'api'
。
// 代码块元数据 标题="apis.js"
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query';
const apiOne = createApi({
reducerPath: 'apiOne',
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (builder) => ({
// ...endpoints
}),
});
const apiTwo = createApi({
reducerPath: 'apiTwo',
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (builder) => ({
// ...endpoints
}),
});
serializeQueryArgs
如果你需要出于任何原因更改缓存键的创建,可以接受一个自定义函数。
默认情况下,此函数将获取查询参数,对适用的对象键进行排序,将结果字符串化,并将其与端点名称连接起来。这将根据参数+端点名称的组合(忽略对象键顺序)创建一个缓存键,以便调用任何给定的端点与相同的参数将产生相同的缓存键。
keepUnusedDataFor
默认为 60
(此值以秒为单位)。这是 RTK Query 在最后一个组件取消订阅 之后 保留你的数据缓存的时间。例如,如果你查询一个端点,然后卸载组件,然后在给定的时间框架内挂载另一个发出相同请求的组件,最近的值将从缓存中提供。
// 代码块元数据 标题="keepUnusedDataFor 示例"
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => 'posts',
keepUnusedDataFor: 5
})
})
})
refetchOnMountOrArgChange
默认为 false
。此设置允许你控制如果已经有缓存结果可用,RTK Query 是否只提供缓存结果,或者如果设置为 true
或者自上次成功查询结果以来已经过去了足够的时间,它应该 refetch
。
false
- 不会导致执行查询,除非 它还不存在。true
- 当添加一个新的查询订阅者时,总是会重新获取。行为与调用refetch
回调或在动作创建器中传递forceRefetch: true
相同。number
- 值以秒为单位。如果提供了一个数字并且缓存中存在一个现有的查询,它将比较当前时间与最后一次满足的时间戳,并且只有在过去了足够的时间后才会重新获取。
如果你在 skip: true
旁边指定了此选项,这个选项在 skip
为 false 之前不会被评估。
你可以在 createApi
中全局设置这个,但你也可以通过将 refetchOnMountOrArgChange
传递给每个单独的 hook 调用或者通过在分发 initiate
动作时传递 forceRefetch: true
来覆盖默认值并有更精细的控制。
refetchOnFocus
默认为 false
。此设置允许你控制当应用程序窗口重新获得焦点后,RTK Query 是否会尝试重新获取所有订阅的查询。
如果你在 skip: true
旁边指定了此选项,这个选项在 skip
为 false 之前不会被评估。
注意:需要调用 setupListeners
。
你可以在 createApi
中全局设置这个,但你也可以通过将 refetchOnFocus
传递给每个单独的 hook 调用或者在分发 initiate
动作时覆盖默认值并有更精细的控制。
如果你在手动分发查询时指定 track: false
,RTK Query 将无法为你自动重新获取。
refetchOnReconnect
默认为 false
。此设置允许你控制当重新获得网络连接后,RTK Query 是否会尝试重新获取所有订阅的查询。
如果你在 skip: true
旁边指定了此选项,这个选项在 skip
为 false 之前不会被评估。
注意:需要调用 setupListeners
。
你可以在 createApi
中全局设置这个,但你也可以通过将 refetchOnReconnect
传递给每个单独的 hook 调用或者在分发 initiate
动作时覆盖默认值并有更精细的控制。
如果你在手动分发查询时指定 track: false
,RTK Query 将无法为你自动重新获取。
端点的解剖
query
(如果没有提供 queryFn
,则必需)
export type query = <QueryArg>(
arg: QueryArg,
) => string | Record<string, unknown>
// with `fetchBaseQuery`
export type query = <QueryArg>(arg: QueryArg) => string | FetchArgs
query
可以是一个返回 string
或传递给你的 baseQuery
的 object
的函数。如果你使用 fetchBaseQuery,这可以返回一个 string
或 FetchArgs
属性的 object
。如果你使用自定义的 baseQuery
,你可以根据自己的喜好定制这个行为。
// 代码块元数据 标题="query 示例"
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Post'],
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => 'posts',
}),
addPost: build.mutation<Post, Partial<Post>>({
query: (body) => ({
url: `posts`,
method: 'POST',
body,
}),
invalidatesTags: [{ type: 'Post', id: 'LIST' }],
}),
})
})
queryFn
(如果没有提供 query
,则必需)
可以用于替代 query
作为一个内联函数,该函数完全绕过了端点的 baseQuery
。
与 baseQuery
的相同参数一起调用,以及提供的 baseQuery
函数本身。它预期返回一个具有 data
或 error
属性的对象,或者一个解析为返回此类对象的 promise。
参见 使用 queryFn 自定义查询。
queryFn(
arg: QueryArg,
api: BaseQueryApi,
extraOptions: BaseQueryExtraOptions<BaseQuery>,
baseQuery: (arg: Parameters<BaseQuery>[0]) => ReturnType<BaseQuery>
): MaybePromise<
| {
error: BaseQueryError<BaseQuery>
data?: undefined
}
| {
error?: undefined
data: ResultType
}
>
export interface BaseQueryApi {
signal: AbortSignal
dispatch: ThunkDispatch<any, any, any>
getState: () => unknown
}
queryFn 函数参数
args
- 调用查询本身时提供的参数api
-BaseQueryApi
对象,包含signal
、dispatch
和getState
属性signal
- 可用于中止 DOM 请求和/或读取请求是否被中止的AbortSignal
对象。dispatch
- 对应 Redux 存储的store.dispatch
方法getState
- 可调用的函数,用于访问当前存储状态
extraOptions
- 为端点提供的可选extraOptions
属性的值baseQuery
- 提供给 api 本身的baseQuery
函数
// 代码块元数据 标题="基础 queryFn 示例"
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => 'posts',
}),
flipCoin: build.query<'heads' | 'tails', void>({
queryFn(arg, queryApi, extraOptions, baseQuery) {
const randomVal = Math.random()
if (randomVal < 0.45) {
return { data: 'heads' }
}
if (randomVal < 0.9) {
return { data: 'tails' }
}
return { error: { status: 500, statusText: 'Internal Server Error', data: "Coin landed on it's edge!" } }
}
})
})
})
transformResponse
(可选,与 queryFn
不适用)
用于操作查询或突变返回的数据的函数。
在某些情况下,你可能希望在将查询返回的数据放入缓存之前对其进行操作。在这种情况下,你可以利用 transformResponse
。
参见 使用 transformResponse
自定义查询响应
transformResponse: (response, meta, arg) =>
response.some.deeply.nested.collection
transformErrorResponse
(可选,与 queryFn
不适用)
用于操作失败的查询或突变返回的数据的函数。
在某些情况下,你可能希望在将查询返回的错误放入缓存之前对其进行操作。在这种情况下,你可以利用 transformErrorResponse
。
参见 使用 transformErrorResponse
自定义查询响应
transformErrorResponse: (response, meta, arg) =>
response.data.some.deeply.nested.errorObject
extraOptions
(可选)
作为第三个参数传递给提供的 baseQuery
函数
providesTags
( 可选,仅适用于查询端点)
由 query
端点使用。确定哪个 'tag' 附加到查询返回的缓存数据上。
期望一个标签类型字符串的数组,一个带有 id 的标签类型对象的数组,或者一个返回这样的数组的函数。
['Post']
- 等同于2
[{ type: 'Post' }]
- 等同于1
[{ type: 'Post', id: 1 }]
(result, error, arg) => ['Post']
- 等同于5
(result, error, arg) => [{ type: 'Post' }]
- 等同于4
(result, error, arg) => [{ type: 'Post', id: 1 }]
参见 提供缓存数据。
// 代码块元数据 标题="providesTags 示例"
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => 'posts',
providesTags: (result) =>
result
? [
...result.map(({ id }) => ({ type: 'Posts' as const, id })),
{ type: 'Posts', id: 'LIST' },
]
: [{ type: 'Posts', id: 'LIST' }],
})
})
})
invalidatesTags
(可选,仅适用于变更端点)
用于 mutation
端点。确定应该重新获取哪 些缓存数据,或者从缓存中删除哪些数据。
期望与 providesTags
相同的形状。
参见 使缓存数据无效。
// 代码块元数据 标题="invalidatesTags 示例"
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query<PostsResponse, 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: `posts`,
method: 'POST',
body,
}
},
invalidatesTags: [{ type: 'Posts', id: 'LIST' }],
}),
})
})
keepUnusedDataFor
(可选,仅适用于查询端点)
仅对此端点覆盖 api-wide 的 keepUnusedDataFor
定义。
默认为 60
(此值以秒为单位)。这是 RTK Query 在最后一个组件取消订阅 之后 保留你的数据缓存的时间。例如,如果你查询一个端点,然后卸载组件,然后在给定的时间框架内挂载另一个发出相同请求的组件,最近的值将从缓存中提供。
// 代码块元数据 标题="keepUnusedDataFor 示例"
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => 'posts',
keepUnusedDataFor: 5
})
})
})
serializeQueryArgs
(可选,仅适用于查询端点)
可以提供一个自定义的缓存键值,基于查询参数。
这主要是为了处理非序列化值作为查询参数对象的一部分传递,并且应该从缓存键中排除的情况。它也可以用于一个端点应该只有一个缓存条目的情况,比如无限加载/分页实现。
与只能返回字符串的 createApi
版本不同,这个每个端点的选项也可以返回一个对象、数字或布尔值。如果它返回一个字符串,那么这个值将直接用作缓存键。如果它返回一个对象/数字/布尔值,那么这个值将被传递给内置的 defaultSerializeQueryArgs
。这简化了剔除你不希望包含在缓存键中的参数的用例。
// 代码块元数据 标题="serializeQueryArgs : 排除值"
import { createApi, fetchBaseQuery, defaultSerializeQueryArgs } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
interface MyApiClient {
fetchPost: (id: string) => Promise<Post>
}
createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
// 示例:一个端点,API 客户端作为参数传入,
// 但只有项目 ID 应该被用作缓存键
getPost: build.query<Post, { id: string; client: MyApiClient }>({
queryFn: async ({ id, client }) => {
const post = await client.fetchPost(id)
return { data: post }
},
serializeQueryArgs: ({ queryArgs, endpointDefinition, endpointName }) => {
const { id } = queryArgs
// 这可以返回一个字符串、一个对象、一个数字或一个布尔值。
// 如果它返回一个对象、数字或布尔值,那个值
// 将通过 `defaultSerializeQueryArgs` 自动序列化
return { id } // 从缓存键中省略 `client`
// 或者,你可以自己使用 `defaultSerializeQueryArgs`:
// return defaultSerializeQueryArgs({
// endpointName,
// queryArgs: { id },
// endpointDefinition
// })
// 或者创建并返回一个字符串:
// return `getPost(${id})`
},
}),
}),
})
merge
(可选,仅适用于查询端点)
可以提供一个将传入的响应值合并到当前缓存数据的方法。 如果提供了,将不会应用自动的结构共享 - 你需要适当地更新缓存。
由于 RTKQ 通常用新的响应替换缓存条目,你通常
需要与 serializeQueryArgs
或 forceRefetch
选项一起使用这个,
以保持现有的缓存条目,以便它可以被更新。
由于这是用 Immer 包装的,你可以直接修改 currentCacheValue
,
或者返回一个新的值,但不能同时做两者。
只有当现有的 currentCacheData
不是 undefined
时才会被调用 - 在第一次响应时,
缓存条目将直接保存响应数据。
如果你不希望新的请求完全覆盖当前的缓存值, 可能是因为你已经从另一个源手动更新了它,不希望这些 更新丢失,这将非常有用。
// 代码块元数据 标题="merge: 分页"
import { createApi, fetchBaseQuery, defaultSerializeQueryArgs } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
listItems: build.query<string[], number>({
query: (pageNumber) => `/listItems?page=${pageNumber}`,
// 因为参数总是映射到一个字符串,所以只有一个缓存条目
serializeQueryArgs: ({ endpointName }) => {
return endpointName
},
// 总是将传入的数据合并到缓存条目
merge: (currentCache, newItems) => {
currentCache.push(...newItems)
},
// 当页面参数改变时重新获取
forceRefetch({ currentArg, previousArg }) {
return currentArg !== previousArg
},
}),
}),
})
forceRefetch
(可选,仅适用于查询端点)
type forceRefetch = (params: {
currentArg: QueryArg | undefined
previousArg: QueryArg | undefined
state: RootState<any, any, string>
endpointState?: QuerySubState<any>
}) => boolean
检查在通常情况下不会强制刷新的情况下,端点是否应该强制刷新。
这主要用于 "无限滚动" / 分页的情况,其中
RTKQ 保持一个 随着时间增加的单一缓存条目,结合
serializeQueryArgs
返回一个固定的缓存键和一个 merge
回调
每次都将传入的数据添加到缓存条目中。
// 代码块元数据 标题="forceRefresh: 分页"
import { createApi, fetchBaseQuery, defaultSerializeQueryArgs } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
listItems: build.query<string[], number>({
query: (pageNumber) => `/listItems?page=${pageNumber}`,
// 因为参数总是映射到一 个字符串,所以只有一个缓存条目
serializeQueryArgs: ({ endpointName }) => {
return endpointName
},
// 总是将传入的数据合并到缓存条目
merge: (currentCache, newItems) => {
currentCache.push(...newItems)
},
// 当页面参数改变时重新获取
forceRefetch({ currentArg, previousArg }) {
return currentArg !== previousArg
},
}),
}),
})
onQueryStarted
(可选)
当你开始每个单独的查询或变更时调用的函数。该函数被调用时会带有一个生命周期 api 对象,其中包含如 queryFulfilled
等属性,允许在查询开始、成功和失败时(即在单个查询/变更调用的整个生命周期中)运行代码。
可以在 mutations
中用于 乐观更新。
生命周期 API 属性
dispatch
- 存储的调度方法。getState
- 获取存储的当前状态的方法。extra
- 作为thunk.extraArgument
提供给configureStore
的getDefaultMiddleware
选项的extra
。requestId
- 为查询/变更生成的唯一 ID。queryFulfilled
- 一个 Promise,将解析为一个带有data
属性(转换后的 查询结果)和一个meta
属性(baseQuery
返回的meta
)。如果查询失败,此 Promise 将拒绝错误。这允许你await
查询完成。getCacheEntry
- 获取当前缓存条目值的函数。updateCachedData
(仅查询端点) - 一个接受 'recipe' 回调的函数,指定如何在调用时更新对应缓存的数据。这在内部使用immer
,并且可以安全地编写 '可变' 的更新,同时生成下一个不可变状态。
async function onQueryStarted(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
queryFulfilled,
getCacheEntry,
}: MutationLifecycleApi,
): Promise<void>
async function onQueryStarted(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
queryFulfilled,
getCacheEntry,
updateCachedData, // 仅查询端点可用
}: QueryLifecycleApi,
): Promise<void>
// 文件: notificationsSlice.ts noEmit
export const messageCreated = (msg: string) => ({
type: 'notifications/messageCreated',
payload: msg,
})
// 文件: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import { messageCreated } from './notificationsSlice'
export interface Post {
id: number
name: string
}
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: '/',
}),
endpoints: (build) => ({
getPost: build.query<Post, number>({
query: (id) => `post/${id}`,
async onQueryStarted(id, { dispatch, queryFulfilled }) {
// `onStart` 副作用
dispatch(messageCreated('正在获取帖子...'))
try {
const { data } = await queryFulfilled
// `onSuccess` 副作用
dispatch(messageCreated('帖子已收到!'))
} catch (err) {
// `onError` 副作用
dispatch(messageCreated('获取帖子出错!'))
}
},
}),
}),
})
onCacheEntryAdded
(可选)
当添加新的缓存条目时调用的函数,即当为端点 + 查询参数组合创建新的订阅时。该函数会被一个生命周期 api 对象调用,该对象包含如 cacheDataLoaded
和 cacheDataRemoved
等属性, 允许在添加缓存条目、加载缓存数据和移除缓存条目(即在缓存条目的整个生命周期)时运行代码。
可以用于流式更新。
缓存生命周期 API 属性
dispatch
- 存储的 dispatch 方法。getState
- 获取存储当前状态的方法。extra
- 作为thunk.extraArgument
提供给configureStore
的getDefaultMiddleware
选项的extra
。requestId
- 为缓存条目生成的唯一 ID。cacheEntryRemoved
- 允许您等待缓存条目从缓存中被移除的时刻的 Promise,这是由于在应用程序中长时间未使用/订阅,或者通过分派api.util.resetApiState
实现的。cacheDataLoaded
- 一个将以此缓存键的第一个值解析的 Promise。这允许您await
直到实际的值在缓存中。注意:如果在任何值被解析之前,缓存条目从缓存中被移除,此 Promise 将以new Error('Promise never resolved before cacheEntryRemoved.')
拒绝,以防止内存泄漏。您可以重新抛出该错误(或者根本不处理它)- 它将在cacheEntryAdded
外部被捕获。getCacheEntry
- 获取缓存条目当前值的函数。updateCachedData
(仅查询端点) - 一个接受 'recipe' 回调的函数,指定如何在调用时更新数据。这在内部使用immer
,并且可以安全地编写 '可变' 的更新,同时生成下一个不可变状态。
async function onCacheEntryAdded(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
cacheEntryRemoved,
cacheDataLoaded,
getCacheEntry,
}: MutationCacheLifecycleApi,
): Promise<void>
async function onCacheEntryAdded(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
cacheEntryRemoved,
cacheDataLoaded,
getCacheEntry,
updateCachedData, // 仅对查询端点可用
}: QueryCacheLifecycleApi,
): Promise<void>