跳到主要内容

 

createApi

createApi 是 RTK Query 功能的核心。它允许你定义一组"端点",描述如何从后端 API 和其他异步源获取数据,包括如何获取和转换这些数据的配置。它生成一个 "API 切片" 结构,该结构包含 Redux 逻辑(可选的 React 钩子),为你封装了数据获取和缓存过程。

提示

通常,你应该只有一个 API 切片用于你的应用需要通信的每个基础 URL。例如,如果你的网站从 /api/posts/api/users 获取数据,你应该有一个单独的 API 切片,其基础 URL 为 /api/,并为 postsusers 定义单独的端点。这样你可以有效地利用自动重新获取功能,通过定义跨端点的 tag 关系。

出于可维护性的考虑,你可能希望在多个文件中分割端点定义,同时仍然维护一个包含所有这些端点的单一 API 切片。请参阅 代码分割 了解如何使用 injectEndpoints 属性将 API 端点从其他文件注入到单一 API 切片定义中。

示例:src/services/pokemon.ts
// 文件: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 - 请求类型(querymutation)。
    • forced - 表示查询是否被强制。
  • extraOptions - 给定端点提供的可选 extraOptions 属性的值

baseQuery 函数签名

Base Query 签名
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

端点只是你想对服务器执行的一组操作。你使用构建器语法将它们定义为一个对象。有两种基本的端点类型:querymutation

请参阅 端点的解剖 了解单个属性的详细信息。

查询端点定义

查询端点定义
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,则必需)

query 签名
export type query = <QueryArg>(
arg: QueryArg,
) => string | Record<string, unknown>

// with `fetchBaseQuery`
export type query = <QueryArg>(arg: QueryArg) => string | FetchArgs

query 可以是一个返回 string 或传递给你的 baseQueryobject 的函数。如果你使用 fetchBaseQuery,这可以返回一个 stringFetchArgs 属性的 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 函数本身。它预期返回一个具有 dataerror 属性的对象,或者一个解析为返回此类对象的 promise。

参见 使用 queryFn 自定义查询

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 对象,包含 signaldispatchgetState 属性
    • 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 的标签类型对象的数组,或者一个返回这样的数组的函数。

  1. ['Post'] - 等同于 2
  2. [{ type: 'Post' }] - 等同于 1
  3. [{ type: 'Post', id: 1 }]
  4. (result, error, arg) => ['Post'] - 等同于 5
  5. (result, error, arg) => [{ type: 'Post' }] - 等同于 4
  6. (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 通常用新的响应替换缓存条目,你通常 需要与 serializeQueryArgsforceRefetch 选项一起使用这个, 以保持现有的缓存条目,以便它可以被更新。

由于这是用 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

(可选,仅适用于查询端点)

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 提供给 configureStoregetDefaultMiddleware 选项的 extra
  • requestId - 为查询/变更生成的唯一 ID。
  • queryFulfilled - 一个 Promise,将解析为一个带有 data 属性(转换后的查询结果)和一个 meta 属性(baseQuery 返回的 meta)。如果查询失败,此 Promise 将拒绝错误。这允许你 await 查询完成。
  • getCacheEntry - 获取当前缓存条目值的函数。
  • updateCachedData (仅查询端点) - 一个接受 'recipe' 回调的函数,指定如何在调用时更新对应缓存的数据。这在内部使用 immer,并且可以安全地编写 '可变' 的更新,同时生成下一个不可变状态。
Mutation onQueryStarted 签名
async function onQueryStarted(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
queryFulfilled,
getCacheEntry,
}: MutationLifecycleApi,
): Promise<void>
Query onQueryStarted 签名
async function onQueryStarted(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
queryFulfilled,
getCacheEntry,
updateCachedData, // 仅查询端点可用
}: QueryLifecycleApi,
): Promise<void>
onQueryStarted 查询生命周期示例
// 文件: 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 对象调用,该对象包含如 cacheDataLoadedcacheDataRemoved 等属性,允许在添加缓存条目、加载缓存数据和移除缓存条目(即在缓存条目的整个生命周期)时运行代码。

可以用于流式更新

缓存生命周期 API 属性

  • dispatch - 存储的 dispatch 方法。
  • getState - 获取存储当前状态的方法。
  • extra - 作为 thunk.extraArgument 提供给 configureStoregetDefaultMiddleware 选项的 extra
  • requestId - 为缓存条目生成的唯一 ID。
  • cacheEntryRemoved - 允许您等待缓存条目从缓存中被移除的时刻的 Promise,这是由于在应用程序中长时间未使用/订阅,或者通过分派 api.util.resetApiState 实现的。
  • cacheDataLoaded - 一个将以此缓存键的第一个值解析的 Promise。这允许您 await 直到实际的值在缓存中。注意:如果在任何值被解析之前,缓存条目从缓存中被移除,此 Promise 将以 new Error('Promise never resolved before cacheEntryRemoved.') 拒绝,以防止内存泄漏。您可以重新抛出该错误(或者根本不处理它)- 它将在 cacheEntryAdded 外部被捕获。
  • getCacheEntry - 获取缓存条目当前值的函数。
  • updateCachedData (仅查询端点) - 一个接受 'recipe' 回调的函数,指定如何在调用时更新数据。这在内部使用 immer,并且可以安全地编写 '可变' 的更新,同时生成下一个不可变状态。
Mutation onCacheEntryAdded 签名
async function onCacheEntryAdded(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
cacheEntryRemoved,
cacheDataLoaded,
getCacheEntry,
}: MutationCacheLifecycleApi,
): Promise<void>
Query onCacheEntryAdded 签名
async function onCacheEntryAdded(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
cacheEntryRemoved,
cacheDataLoaded,
getCacheEntry,
updateCachedData, // 仅对查询端点可用
}: QueryCacheLifecycleApi,
): Promise<void>

返回值

参见创建的 Api" API 参考