createApi
createApi
是 RTK Query 功能的核心。它允许您定义一组“端点”,这些端点描述如何从后端 API 和其他异步来源检索数据,包括配置如何获取和转换这些数据。它生成一个“API 切片”结构,其中包含 Redux 逻辑(以及可选的 React Hooks),这些逻辑封装了为您进行数据获取和缓存的过程。
- TypeScript
- JavaScript
// Need to use the React-specific entry point to allow generating React hooks
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Pokemon } from './types'
// Define a service using a base URL and expected endpoints
export const pokemonApi = createApi({
reducerPath: 'pokemonApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query<Pokemon, string>({
query: (name) => `pokemon/${name}`,
}),
}),
})
// Export hooks for usage in function components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = pokemonApi
// Need to use the React-specific entry point to allow generating React hooks
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
// Define a service using a base URL and expected endpoints
export const pokemonApi = createApi({
reducerPath: 'pokemonApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query({
query: (name) => `pokemon/${name}`,
}),
}),
})
// Export hooks for usage in function components, which are
// auto-generated based on the defined endpoints
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; // value is in seconds
refetchOnMountOrArgChange?: boolean | number; // value is in seconds
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
}
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
// ...endpoints
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
// ...endpoints
}),
})
endpoints
端点只是一组您想要对服务器执行的操作。您可以使用构建器语法将它们定义为一个对象。有两种基本端点类型:query
和 mutation
。
有关各个属性的详细信息,请参见 端点的结构。
查询端点定义
export type QueryDefinition<
QueryArg,
BaseQuery extends BaseQueryFn,
TagTypes extends string,
ResultType,
ReducerPath extends string = string,
> = {
query(arg: QueryArg): BaseQueryArg<BaseQuery>
/* either `query` or `queryFn` can be present, but not both simultaneously */
queryFn(
arg: QueryArg,
api: BaseQueryApi,
extraOptions: BaseQueryExtraOptions<BaseQuery>,
baseQuery: (arg: Parameters<BaseQuery>[0]) => ReturnType<BaseQuery>,
): MaybePromise<QueryReturnValue<ResultType, BaseQueryError<BaseQuery>>>
/* transformResponse only available with `query`, not `queryFn` */
transformResponse?(
baseQueryReturnValue: BaseQueryResult<BaseQuery>,
meta: BaseQueryMeta<BaseQuery>,
arg: QueryArg,
): ResultType | Promise<ResultType>
/* transformErrorResponse only available with `query`, not `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, // available for query endpoints only
}: QueryLifecycleApi,
): Promise<void>
onCacheEntryAdded?(
arg: QueryArg,
{
dispatch,
getState,
extra,
requestId,
cacheEntryRemoved,
cacheDataLoaded,
getCacheEntry,
updateCachedData, // available for query endpoints only
}: 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>
/* either `query` or `queryFn` can be present, but not both simultaneously */
queryFn(
arg: QueryArg,
api: BaseQueryApi,
extraOptions: BaseQueryExtraOptions<BaseQuery>,
baseQuery: (arg: Parameters<BaseQuery>[0]) => ReturnType<BaseQuery>,
): MaybePromise<QueryReturnValue<ResultType, BaseQueryError<BaseQuery>>>
/* transformResponse only available with `query`, not `queryFn` */
transformResponse?(
baseQueryReturnValue: BaseQueryResult<BaseQuery>,
meta: BaseQueryMeta<BaseQuery>,
arg: QueryArg,
): ResultType | Promise<ResultType>
/* transformErrorResponse only available with `query`, not `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
。
- TypeScript
- JavaScript
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'],
}),
}),
})
// Auto-generated hooks
export const { useGetPostsQuery, useAddPostMutation } = api
// Possible exports
export const { endpoints, reducerPath, reducer, middleware } = api
// reducerPath, reducer, middleware are only used in store configuration
// endpoints will have:
// endpoints.getPosts.initiate(), endpoints.getPosts.select(), endpoints.getPosts.useQuery()
// endpoints.addPost.initiate(), endpoints.addPost.select(), endpoints.addPost.useMutation()
// see `createApi` overview for _all exports_
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query({
query: () => 'posts',
providesTags: (result) =>
result ? result.map(({ id }) => ({ type: 'Posts', id })) : [],
}),
addPost: build.mutation({
query: (body) => ({
url: `posts`,
method: 'POST',
body,
}),
invalidatesTags: ['Posts'],
}),
}),
})
// Auto-generated hooks
export const { useGetPostsQuery, useAddPostMutation } = api
// Possible exports
export const { endpoints, reducerPath, reducer, middleware } = api
// reducerPath, reducer, middleware are only used in store configuration
// endpoints will have:
// endpoints.getPosts.initiate(), endpoints.getPosts.select(), endpoints.getPosts.useQuery()
// endpoints.addPost.initiate(), endpoints.addPost.select(), endpoints.addPost.useMutation()
// see `createApi` overview for _all exports_
extractRehydrationInfo
一个传递给每个分派的 action 的函数。如果它返回的值不是 undefined
,则该返回值将用于重新水合已完成和出错的查询。
- TypeScript
- JavaScript
import type { Action, PayloadAction } from '@reduxjs/toolkit'
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { HYDRATE } from 'next-redux-wrapper'
type RootState = any // normally inferred from state
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) => ({
// omitted
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { HYDRATE } from 'next-redux-wrapper'
function isHydrateAction(action) {
return action.type === HYDRATE
}
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
extractRehydrationInfo(action, { reducerPath }) {
if (isHydrateAction(action)) {
return action.payload[reducerPath]
}
},
endpoints: (build) => ({
// omitted
}),
})
tagTypes
一个字符串标签类型名称数组。指定标签类型是可选的,但您应该定义它们以便它们可以用于缓存和失效。当定义标签类型时,您将能够在配置 端点 时使用 providesTags
提供 它们,并使用 invalidatesTags
失效 它们。
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
// ...endpoints
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
// ...endpoints
}),
})
reducerPath
reducerPath
是一个唯一的键,您的服务将被挂载到您的存储中。如果您在应用程序中多次调用 createApi
,则每次都需要提供一个唯一的值。默认值为 'api'
。
- TypeScript
- JavaScript
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
}),
})
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 在最后一个组件取消订阅后保留数据的时长。例如,如果您查询一个端点,然后卸载组件,然后在给定的时间范围内挂载另一个执行相同请求的组件,则将从缓存中提供最新的值。
- TypeScript
- JavaScript
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,
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPosts: build.query({
query: () => 'posts',
keepUnusedDataFor: 5,
}),
}),
})
refetchOnMountOrArgChange
默认为 false
。此设置允许您控制如果缓存结果已存在,RTK Query 是否只提供缓存结果,或者如果设置为 true
或者自上次成功查询结果以来已经过去了足够的时间,它是否应该 refetch
。
false
- 除非缓存结果不存在,否则不会执行查询。true
- 当查询的新订阅者被添加时,将始终重新获取。行为与调用refetch
回调或在操作创建者中传递forceRefetch: true
相同。number
- 值为秒。如果提供了一个数字,并且缓存中存在一个现有查询,它将比较当前时间与上次完成的时间戳,并且只有在经过足够的时间后才会重新获取。
如果您在 skip: true
旁边指定此选项,则在 skip
为 false 之前,不会评估此选项。
您可以在 createApi
中全局设置此选项,但您也可以通过将 refetchOnMountOrArgChange
传递给每个单独的钩子调用,或者类似地通过在调度 initiate
操作时传递 forceRefetch: true
来覆盖默认值并获得更细粒度的控制。
refetchOnFocus
默认值为 false
。此设置允许您控制 RTK Query 在应用程序窗口重新获得焦点后是否尝试重新获取所有已订阅的查询。
如果您在 skip: true
旁边指定此选项,则在 skip
为 false 之前,不会评估此选项。
注意:需要调用 setupListeners
。
您可以在 createApi
中全局设置此值,但您也可以覆盖默认值并通过将 refetchOnFocus
传递给每个单独的钩子调用或在分派 initiate
操作时获得更细粒度的控制。
如果您在手动分派查询时指定 track: false
,RTK Query 将无法自动为您重新获取。
refetchOnReconnect
默认值为 false
。此设置允许您控制 RTK Query 在重新获得网络连接后是否尝试重新获取所有已订阅的查询。
如果您在 skip: true
旁边指定此选项,则在 skip
为 false 之前,不会评估此选项。
注意:需要调用 setupListeners
。
您可以在 createApi
中全局设置此值,但您也可以覆盖默认值并通过将 refetchOnReconnect
传递给每个单独的钩子调用或在分派 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
或一个 object
,该对象将传递给您的 baseQuery
。如果您使用的是 fetchBaseQuery,则它可以返回 FetchArgs
中的属性的 string
或 object
。如果您使用自己的自定义 baseQuery
,您可以根据自己的喜好自定义此行为。
- TypeScript
- JavaScript
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' }],
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Post'],
endpoints: (build) => ({
getPosts: build.query({
query: () => 'posts',
}),
addPost: build.mutation({
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
- 包含signal
、dispatch
和getState
属性的BaseQueryApi
对象signal
- 一个AbortSignal
对象,可用于中止 DOM 请求和/或读取请求是否已中止。dispatch
- 对应 Redux 存储的store.dispatch
方法getState
- 一个可以被调用以访问当前存储状态的函数
extraOptions
- 为端点提供的可选extraOptions
属性的值baseQuery
- 提供给 api 本身的baseQuery
函数
- TypeScript
- JavaScript
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!",
},
}
},
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPosts: build.query({
query: () => 'posts',
}),
flipCoin: build.query({
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
端点使用。确定将哪个“标签”附加到查询返回的缓存数据。期望一个标签类型字符串数组,一个包含标签类型和 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 }]
另请参阅 提供缓存数据.
- TypeScript
- JavaScript
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' }],
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query({
query: () => 'posts',
providesTags: (result) =>
result
? [
...result.map(({ id }) => ({ type: 'Posts', id })),
{ type: 'Posts', id: 'LIST' },
]
: [{ type: 'Posts', id: 'LIST' }],
}),
}),
})
invalidatesTags
(可选,仅用于 mutation 终结点)
由 mutation
终结点使用。确定哪些缓存数据应该重新获取或从缓存中删除。期望与 providesTags
相同的形状。
另请参阅 使缓存数据失效。
- TypeScript
- JavaScript
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' }],
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query({
query: () => 'posts',
providesTags: (result) =>
result
? [
...result.map(({ id }) => ({ type: 'Posts', id })),
{ type: 'Posts', id: 'LIST' },
]
: [{ type: 'Posts', id: 'LIST' }],
}),
addPost: build.mutation({
query(body) {
return {
url: `posts`,
method: 'POST',
body,
}
},
invalidatesTags: [{ type: 'Posts', id: 'LIST' }],
}),
}),
})
keepUnusedDataFor
(可选,仅适用于查询端点)
仅为此终结点覆盖 keepUnusedDataFor
的 API 范围定义。
默认为 60
(此值为秒)。这是 RTK Query 在最后一个组件取消订阅后保留数据的时长。例如,如果您查询一个端点,然后卸载组件,然后在给定的时间范围内挂载另一个执行相同请求的组件,则将从缓存中提供最新的值。
- TypeScript
- JavaScript
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,
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPosts: build.query({
query: () => 'posts',
keepUnusedDataFor: 5,
}),
}),
})
serializeQueryArgs
(可选,仅适用于查询端点)
可以提供以根据查询参数返回自定义缓存键值。
这主要用于查询参数对象中传递了不可序列化值的情况,并且应该从缓存键中排除。它也可以用于终结点应该只有一个缓存条目,例如无限加载/分页实现。
与 createApi
版本不同,该版本只能返回字符串,此每个终结点的选项也可以返回对象、数字或布尔值。如果它返回字符串,该值将直接用作缓存键。如果它返回对象/数字/布尔值,该值将传递给内置的 defaultSerializeQueryArgs
。这简化了从缓存键中排除不想包含的参数的使用情况。
- TypeScript
- JavaScript
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) => ({
// Example: an endpoint with an API client passed in as an argument,
// but only the item ID should be used as the cache key
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
// This can return a string, an object, a number, or a boolean.
// If it returns an object, number or boolean, that value
// will be serialized automatically via `defaultSerializeQueryArgs`
return { id } // omit `client` from the cache key
// Alternately, you can use `defaultSerializeQueryArgs` yourself:
// return defaultSerializeQueryArgs({
// endpointName,
// queryArgs: { id },
// endpointDefinition
// })
// Or create and return a string yourself:
// return `getPost(${id})`
},
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
// Example: an endpoint with an API client passed in as an argument,
// but only the item ID should be used as the cache key
getPost: build.query({
queryFn: async ({ id, client }) => {
const post = await client.fetchPost(id)
return { data: post }
},
serializeQueryArgs: ({ queryArgs, endpointDefinition, endpointName }) => {
const { id } = queryArgs
// This can return a string, an object, a number, or a boolean.
// If it returns an object, number or boolean, that value
// will be serialized automatically via `defaultSerializeQueryArgs`
return { id } // omit `client` from the cache key
// Alternately, you can use `defaultSerializeQueryArgs` yourself:
// return defaultSerializeQueryArgs({
// endpointName,
// queryArgs: { id },
// endpointDefinition
// })
// Or create and return a string yourself:
// return `getPost(${id})`
},
}),
}),
})
merge
(可选,仅适用于查询端点)
可以提供将传入的响应值合并到当前缓存数据中。如果提供,将不会应用自动结构共享 - 由您负责适当地更新缓存。
由于 RTKQ 通常用新响应替换缓存条目,因此您通常需要将它与 serializeQueryArgs
或 forceRefetch
选项一起使用以保留现有缓存条目,以便可以更新它。
由于这是用 Immer 包装的,您可以直接修改 currentCacheValue
,也可以返回一个新值,但不能同时进行两者。
仅当现有 currentCacheData
不是 undefined
时才会调用 - 在第一次响应时,缓存条目将直接保存响应数据。
如果您不希望新的请求完全覆盖当前缓存值,这很有用,也许是因为您已从另一个来源手动更新了它,并且不希望这些更新丢失。
- TypeScript
- JavaScript
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}`,
// Only have one cache entry because the arg always maps to one string
serializeQueryArgs: ({ endpointName }) => {
return endpointName
},
// Always merge incoming data to the cache entry
merge: (currentCache, newItems) => {
currentCache.push(...newItems)
},
// Refetch when the page arg changes
forceRefetch({ currentArg, previousArg }) {
return currentArg !== previousArg
},
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
listItems: build.query({
query: (pageNumber) => `/listItems?page=${pageNumber}`,
// Only have one cache entry because the arg always maps to one string
serializeQueryArgs: ({ endpointName }) => {
return endpointName
},
// Always merge incoming data to the cache entry
merge: (currentCache, newItems) => {
currentCache.push(...newItems)
},
// Refetch when the page arg changes
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
回调,每次都将传入数据添加到缓存条目中。
- TypeScript
- JavaScript
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}`,
// Only have one cache entry because the arg always maps to one string
serializeQueryArgs: ({ endpointName }) => {
return endpointName
},
// Always merge incoming data to the cache entry
merge: (currentCache, newItems) => {
currentCache.push(...newItems)
},
// Refetch when the page arg changes
forceRefetch({ currentArg, previousArg }) {
return currentArg !== previousArg
},
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
listItems: build.query({
query: (pageNumber) => `/listItems?page=${pageNumber}`,
// Only have one cache entry because the arg always maps to one string
serializeQueryArgs: ({ endpointName }) => {
return endpointName
},
// Always merge incoming data to the cache entry
merge: (currentCache, newItems) => {
currentCache.push(...newItems)
},
// Refetch when the page arg changes
forceRefetch({ currentArg, previousArg }) {
return currentArg !== previousArg
},
}),
}),
})
onQueryStarted
(可选)
一个在您启动每个单独的查询或变异时调用的函数。该函数使用包含 queryFulfilled
等属性的生命周期 API 对象调用,允许在启动查询、查询成功和查询失败时(即在单个查询/变异调用的整个生命周期中)运行代码。
可以在 mutations
中用于 乐观更新。
生命周期 API 属性
dispatch
- 存储的 dispatch 方法。getState
- 获取存储当前状态的方法。extra
- 作为thunk.extraArgument
提供给configureStore
getDefaultMiddleware
选项的extra
。requestId
- 为查询/变异生成的唯一 ID。queryFulfilled
- 一个 Promise,它将解析为一个data
属性(转换后的查询结果)和一个meta
属性(baseQuery
返回的meta
)。如果查询失败,此 Promise 将使用错误拒绝。这允许您await
查询完成。getCacheEntry
- 获取缓存条目当前值的函数。updateCachedData
(仅限查询端点) - 一个函数,它接受一个“配方”回调,指定如何在调用时更新相应缓存的数据。这在内部使用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, // available for query endpoints only
}: QueryLifecycleApi,
): Promise<void>
- TypeScript
- JavaScript
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` side-effect
dispatch(messageCreated('Fetching post...'))
try {
const { data } = await queryFulfilled
// `onSuccess` side-effect
dispatch(messageCreated('Post received!'))
} catch (err) {
// `onError` side-effect
dispatch(messageCreated('Error fetching post!'))
}
},
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import { messageCreated } from './notificationsSlice'
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: '/',
}),
endpoints: (build) => ({
getPost: build.query({
query: (id) => `post/${id}`,
async onQueryStarted(id, { dispatch, queryFulfilled }) {
// `onStart` side-effect
dispatch(messageCreated('Fetching post...'))
try {
const { data } = await queryFulfilled
// `onSuccess` side-effect
dispatch(messageCreated('Post received!'))
} catch (err) {
// `onError` side-effect
dispatch(messageCreated('Error fetching post!'))
}
},
}),
}),
})
onCacheEntryAdded
(可选)
当添加新的缓存条目时调用此函数,即当为端点 + 查询参数组合创建新的订阅时。该函数将使用包含 cacheDataLoaded
和 cacheDataRemoved
等属性的生命周期 API 对象调用,允许在添加缓存条目、加载缓存数据以及删除缓存条目时(即在缓存条目的整个生命周期中)运行代码。
可用于 流式更新。
缓存生命周期 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
(仅查询端点) - 一个接受“配方”回调的函数,指定如何在调用时更新数据。这在内部使用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, // available for query endpoints only
}: QueryCacheLifecycleApi,
): Promise<void>