Usage > Customizing Queries: overriding default query behavior">Usage > Customizing Queries: overriding default query behavior">
跳至主要内容

自定义查询

RTK Query 对请求的解析方式没有限制。您可以使用任何您喜欢的库来处理请求,或者根本不使用库。RTK Query 提供了合理的默认值,预计可以涵盖大多数用例,同时还允许自定义查询处理以满足特定需求。

使用 baseQuery 自定义查询

处理查询的默认方法是通过 baseQuery 选项在 createApi 上,结合端点定义上的 query 选项。

为了处理查询,端点通过 query 选项定义,该选项将返回值传递给 API 使用的通用 baseQuery 函数。

默认情况下,RTK Query 附带 fetchBaseQuery,它是一个轻量级的 fetch 包装器,它以类似于 axios 等常用库的方式自动处理请求头和响应解析。如果 fetchBaseQuery 无法满足您的需求,您可以使用包装函数自定义其行为,或者从头开始创建自己的 baseQuery 函数供 createApi 使用。

另请参阅 baseQuery API 参考

实现自定义 baseQuery

RTK Query 预计 baseQuery 函数将使用三个参数调用:argsapiextraOptions。它应该返回一个包含 dataerror 属性的对象,或者返回一个解析为返回此类对象的 Promise。

提示

基本查询和查询函数必须始终自行捕获错误,并将其返回到对象中!

function brokenCustomBaseQuery() {
// ❌ Don't let this throw by itself
const data = await fetchSomeData()
return { data }
}

function correctCustomBaseQuery() {
// ✅ Catch errors and _return_ them so the RTKQ logic can track it
try {
const data = await fetchSomeData()
return { data }
} catch (error) {
return { error }
}
}

baseQuery 函数参数

baseQuery 示例参数
const customBaseQuery = (
args,
{ signal, dispatch, getState },
extraOptions,
) => {
// omitted
}

baseQuery 函数返回值

  1. 预期成功结果格式
    return { data: YourData }
  2. 预期错误结果格式
    return { error: YourError }
baseQuery 示例返回值
const customBaseQuery = (
args,
{ signal, dispatch, getState },
extraOptions,
) => {
if (Math.random() > 0.5) return { error: 'Too high!' }
return { data: 'All good!' }
}
注意

此格式是必需的,以便 RTK Query 可以推断响应的返回类型。

从本质上讲,baseQuery 函数只需要具有最小的有效返回值;一个包含 dataerror 属性的对象。由用户决定如何使用提供的参数,以及如何在函数本身内处理请求。

fetchBaseQuery 默认值

对于 fetchBaseQuery 来说,返回类型如下

fetchBaseQuery 的返回类型
Promise<
| {
data: any
error?: undefined
meta?: { request: Request; response: Response }
}
| {
error: {
status: number
data: any
}
data?: undefined
meta?: { request: Request; response: Response }
}
>
  1. 使用 fetchBaseQuery 预期的成功结果格式
    return { data: YourData }
  2. 使用 fetchBaseQuery 预期的错误结果格式
    return { error: { status: number, data: YourErrorData } }

使用 transformResponse 自定义查询响应

createApi 上的单个端点接受一个 transformResponse 属性,它允许在查询或变异返回的数据到达缓存之前对其进行操作。

transformResponse 使用成功 baseQuery 为相应端点返回的数据调用,transformResponse 的返回值用作与该端点调用关联的缓存数据。

默认情况下,服务器的有效负载将直接返回。

function defaultTransformResponse(
baseQueryReturnValue: unknown,
meta: unknown,
arg: unknown
) {
return baseQueryReturnValue
}

要更改它,请提供一个类似于以下的函数

解压缩深度嵌套的集合
transformResponse: (response, meta, arg) =>
response.some.deeply.nested.collection

transformResponse 使用从 baseQuery 返回的 meta 属性作为其第二个参数调用,它可以在确定转换后的响应时使用。meta 的值取决于所使用的 baseQuery

transformResponse meta 示例
transformResponse: (response: { sideA: Tracks; sideB: Tracks }, meta, arg) => {
if (meta?.coinFlip === 'heads') {
return response.sideA
}
return response.sideB
}

transformResponse 使用提供给端点的 arg 属性作为其第三个参数调用,它可以在确定转换后的响应时使用。arg 的值取决于所使用的 endpoint,以及调用查询/变异时使用的参数。

transformResponse arg 示例
transformResponse: (response: Posts, meta, arg) => {
return {
originalArg: arg,
data: response,
}
}

虽然使用 RTK Query 管理缓存数据时,将响应存储在 规范化的查找表 中的需求较少,但如果需要,可以利用 transformResponse 来实现。

规范化响应数据
transformResponse: (response) =>
response.reduce((acc, curr) => {
acc[curr.id] = curr
return acc
}, {})

/*
will convert:
[
{id: 1, name: 'Harry'},
{id: 2, name: 'Ron'},
{id: 3, name: 'Hermione'},
]

to:
{
1: { id: 1, name: "Harry" },
2: { id: 2, name: "Ron" },
3: { id: 3, name: "Hermione" },
}
*/

createEntityAdapter 也可以与 transformResponse 一起使用来规范化数据,同时还可以利用 createEntityAdapter 提供的其他功能,包括提供 ids 数组,使用 sortComparer 来维护始终排序的列表,以及维护强大的 TypeScript 支持。

另请参阅 具有转换后的响应形状的 Websocket 聊天 API,了解 transformResponse 规范化响应数据与 createEntityAdapter 相结合的示例,同时还使用 流式更新 更新更多数据。

使用 transformErrorResponse 自定义查询响应

createApi 上的单个端点接受一个 transformErrorResponse 属性,它允许在查询或变异返回的错误到达缓存之前对其进行操作。

transformErrorResponse 使用失败的 baseQuery 为相应端点返回的错误调用,transformErrorResponse 的返回值用作与该端点调用关联的缓存错误。

默认情况下,服务器的有效负载将直接返回。

function defaultTransformResponse(
baseQueryReturnValue: unknown,
meta: unknown,
arg: unknown
) {
return baseQueryReturnValue
}

要更改它,请提供一个类似于以下的函数

解包深度嵌套的错误对象
transformErrorResponse: (response, meta, arg) =>
response.data.some.deeply.nested.errorObject

transformErrorResponse 被调用时,会将从 baseQuery 返回的 meta 属性作为第二个参数,这可以在确定转换后的响应时使用。meta 的值取决于所使用的 baseQuery

transformErrorResponse meta 示例
transformErrorResponse: (
response: { data: { sideA: Tracks; sideB: Tracks } },
meta,
arg,
) => {
if (meta?.coinFlip === 'heads') {
return response.data.sideA
}
return response.data.sideB
}

transformErrorResponse 被调用时,会将提供给端点的 arg 属性作为第三个参数,这可以在确定转换后的响应时使用。arg 的值取决于所使用的 endpoint,以及调用查询/变异时使用的参数。

transformErrorResponse arg 示例
transformErrorResponse: (response: Posts, meta, arg) => {
return {
originalArg: arg,
error: response,
}
}

使用 queryFn 自定义查询

RTK Query 自带 fetchBaseQuery,这使得定义与 HTTP URL(如典型的 REST API)通信的端点变得非常简单。我们还集成了 GraphQL。然而,RTK Query 的核心实际上是关于跟踪任何异步请求/响应序列的加载状态和缓存值,而不仅仅是 HTTP 请求。

RTK Query 支持定义运行任意异步逻辑并返回结果的端点。在 createApi 上的单个端点接受一个 queryFn 属性,它允许您编写自己的异步函数,并在其中包含您想要的任何逻辑。

这对于您希望对单个端点具有特别不同的行为,或者查询本身不相关的场景很有用,包括

  • 使用不同基本 URL 的一次性查询
  • 使用不同请求处理(如自动重试)的一次性查询
  • 使用不同错误处理行为的一次性查询
  • 使用第三方库 SDK(如 Firebase 或 Supabase)进行请求的查询
  • 执行非典型请求/响应的异步任务的查询
  • 使用单个查询执行多个请求 (示例)
  • 利用无效行为,但没有相关的查询 (示例)
  • 使用 流式更新,但没有相关的初始请求 (示例)

另请参阅 queryFn API 参考,了解类型签名和可用选项。

实现 queryFn

queryFn 可以被认为是内联 baseQuery。它将使用与 baseQuery 相同的参数调用,以及提供的 baseQuery 函数本身(argapiextraOptionsbaseQuery)。与 baseQuery 类似,它应该返回一个包含 dataerror 属性的对象,或者返回一个解析为返回此类对象的 promise。

基本 queryFn 示例

基本 queryFn 示例
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import { userAPI, User } from './userAPI'

const api = createApi({
baseQuery: fetchBaseQuery({ url: '/' }),
endpoints: (build) => ({
// normal HTTP endpoint using fetchBaseQuery
getPosts: build.query<PostsResponse, void>({
query: () => ({ url: 'posts' }),
}),
// endpoint with a custom `queryFn` and separate async logic
getUser: build.query<User, string>({
queryFn: async (userId: string) => {
try {
const user = await userApi.getUserById(userId)
// Return the result in an object with a `data` field
return { data: user }
} catch (error) {
// Catch any errors and return them as an object with an `error` field
return { error }
}
},
}),
}),
})

queryFn 函数参数

queryFn 示例参数
const queryFn = (
args,
{ signal, dispatch, getState },
extraOptions,
baseQuery,
) => {
// omitted
}

queryFn 函数返回值

  1. 预期成功结果格式
    return { data: YourData }
  2. 预期错误结果格式
    return { error: YourError }
queryFn 示例返回值
const queryFn = (
args,
{ signal, dispatch, getState },
extraOptions,
baseQuery,
) => {
if (Math.random() > 0.5) return { error: 'Too high!' }
return { data: 'All good!' }
}

示例 - baseQuery

Axios baseQuery

此示例实现了一个非常基本的基于 axios 的 baseQuery 工具。

基本 axios baseQuery
import { createApi } from '@reduxjs/toolkit/query'
import type { BaseQueryFn } from '@reduxjs/toolkit/query'
import axios from 'axios'
import type { AxiosRequestConfig, AxiosError } from 'axios'

const axiosBaseQuery =
(
{ baseUrl }: { baseUrl: string } = { baseUrl: '' }
): BaseQueryFn<
{
url: string
method?: AxiosRequestConfig['method']
data?: AxiosRequestConfig['data']
params?: AxiosRequestConfig['params']
headers?: AxiosRequestConfig['headers']
},
unknown,
unknown
> =>
async ({ url, method, data, params, headers }) => {
try {
const result = await axios({
url: baseUrl + url,
method,
data,
params,
headers,
})
return { data: result.data }
} catch (axiosError) {
const err = axiosError as AxiosError
return {
error: {
status: err.response?.status,
data: err.response?.data || err.message,
},
}
}
}

const api = createApi({
baseQuery: axiosBaseQuery({
baseUrl: 'https://example.com',
}),
endpoints(build) {
return {
query: build.query({ query: () => ({ url: '/query', method: 'get' }) }),
mutation: build.mutation({
query: () => ({ url: '/mutation', method: 'post' }),
}),
}
},
})

GraphQL baseQuery

此示例实现了一个非常基本的基于 GraphQL 的 baseQuery

基本 GraphQL baseQuery
import { createApi } from '@reduxjs/toolkit/query'
import { request, gql, ClientError } from 'graphql-request'

const graphqlBaseQuery =
({ baseUrl }: { baseUrl: string }) =>
async ({ body }: { body: string }) => {
try {
const result = await request(baseUrl, body)
return { data: result }
} catch (error) {
if (error instanceof ClientError) {
return { error: { status: error.response.status, data: error } }
}
return { error: { status: 500, data: error } }
}
}

export const api = createApi({
baseQuery: graphqlBaseQuery({
baseUrl: 'https://graphqlzero.almansi.me/api',
}),
endpoints: (builder) => ({
getPosts: builder.query({
query: () => ({
body: gql`
query {
posts {
data {
id
title
}
}
}
`,
}),
transformResponse: (response) => response.posts.data,
}),
getPost: builder.query({
query: (id) => ({
body: gql`
query {
post(id: ${id}) {
id
title
body
}
}
`,
}),
transformResponse: (response) => response.post,
}),
}),
})

通过扩展 fetchBaseQuery 实现自动重新授权

此示例包装了 fetchBaseQuery,以便在遇到 401 Unauthorized 错误时,发送额外的请求以尝试刷新授权令牌,并在重新授权后重新尝试初始查询。

使用自定义 base query 模拟类似 axios 的拦截器
import { fetchBaseQuery } from '@reduxjs/toolkit/query'
import type {
BaseQueryFn,
FetchArgs,
FetchBaseQueryError,
} from '@reduxjs/toolkit/query'
import { tokenReceived, loggedOut } from './authSlice'

const baseQuery = fetchBaseQuery({ baseUrl: '/' })
const baseQueryWithReauth: BaseQueryFn<
string | FetchArgs,
unknown,
FetchBaseQueryError
> = async (args, api, extraOptions) => {
let result = await baseQuery(args, api, extraOptions)
if (result.error && result.error.status === 401) {
// try to get a new token
const refreshResult = await baseQuery('/refreshToken', api, extraOptions)
if (refreshResult.data) {
// store the new token
api.dispatch(tokenReceived(refreshResult.data))
// retry the initial query
result = await baseQuery(args, api, extraOptions)
} else {
api.dispatch(loggedOut())
}
}
return result
}

防止出现多个未授权错误

使用 async-mutex 防止在多个调用因 401 Unauthorized 错误而失败时,多次调用 '/refreshToken'。

防止多次调用 '/refreshToken'
import { fetchBaseQuery } from '@reduxjs/toolkit/query'
import type {
BaseQueryFn,
FetchArgs,
FetchBaseQueryError,
} from '@reduxjs/toolkit/query'
import { tokenReceived, loggedOut } from './authSlice'
import { Mutex } from 'async-mutex'

// create a new mutex
const mutex = new Mutex()
const baseQuery = fetchBaseQuery({ baseUrl: '/' })
const baseQueryWithReauth: BaseQueryFn<
string | FetchArgs,
unknown,
FetchBaseQueryError
> = async (args, api, extraOptions) => {
// wait until the mutex is available without locking it
await mutex.waitForUnlock()
let result = await baseQuery(args, api, extraOptions)
if (result.error && result.error.status === 401) {
// checking whether the mutex is locked
if (!mutex.isLocked()) {
const release = await mutex.acquire()
try {
const refreshResult = await baseQuery(
'/refreshToken',
api,
extraOptions
)
if (refreshResult.data) {
api.dispatch(tokenReceived(refreshResult.data))
// retry the initial query
result = await baseQuery(args, api, extraOptions)
} else {
api.dispatch(loggedOut())
}
} finally {
// release must be called once the mutex should be released again.
release()
}
} else {
// wait until the mutex is available without locking it
await mutex.waitForUnlock()
result = await baseQuery(args, api, extraOptions)
}
}
return result
}

自动重试

RTK Query 导出一个名为 retry 的实用程序,您可以使用它来包装 API 定义中的 baseQuery。它默认尝试 5 次,并使用基本的指数退避。

默认行为将在以下时间间隔重试

  1. 600ms * random(0.4, 1.4)
  2. 1200ms * random(0.4, 1.4)
  3. 2400ms * random(0.4, 1.4)
  4. 4800ms * random(0.4, 1.4)
  5. 9600ms * random(0.4, 1.4)
默认情况下,每个请求重试 5 次
import { createApi, fetchBaseQuery, retry } from '@reduxjs/toolkit/query/react'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]

// maxRetries: 5 is the default, and can be omitted. Shown for documentation purposes.
const staggeredBaseQuery = retry(fetchBaseQuery({ baseUrl: '/' }), {
maxRetries: 5,
})
export const api = createApi({
baseQuery: staggeredBaseQuery,
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => ({ url: 'posts' }),
}),
getPost: build.query<PostsResponse, string>({
query: (id) => ({ url: `post/${id}` }),
extraOptions: { maxRetries: 8 }, // You can override the retry behavior on each endpoint
}),
}),
})

export const { useGetPostsQuery, useGetPostQuery } = api

如果您不想对特定端点进行重试,只需将 maxRetries 设置为 0 即可。

信息

钩子可以同时返回 dataerror。默认情况下,RTK Query 会将最后一个“良好”结果保留在 data 中,直到它可以被更新或垃圾回收。

退出错误重试

retry 实用程序有一个附加的 fail 方法属性,可用于立即退出重试。这可用于已知额外的重试将保证全部失败且将是冗余的情况。

退出错误重试
import { createApi, fetchBaseQuery, retry } from '@reduxjs/toolkit/query/react'
import type { FetchArgs } from '@reduxjs/toolkit/query'
interface Post {
id: number
name: string
}
type PostsResponse = Post[]

const staggeredBaseQueryWithBailOut = retry(
async (args: string | FetchArgs, api, extraOptions) => {
const result = await fetchBaseQuery({ baseUrl: '/api/' })(
args,
api,
extraOptions
)

// bail out of re-tries immediately if unauthorized,
// because we know successive re-retries would be redundant
if (result.error?.status === 401) {
retry.fail(result.error)
}

return result
},
{
maxRetries: 5,
}
)

export const api = createApi({
baseQuery: staggeredBaseQueryWithBailOut,
endpoints: (build) => ({
getPosts: build.query<PostsResponse, void>({
query: () => ({ url: 'posts' }),
}),
getPost: build.query<Post, string>({
query: (id) => ({ url: `post/${id}` }),
extraOptions: { maxRetries: 8 }, // You can override the retry behavior on each endpoint
}),
}),
})
export const { useGetPostsQuery, useGetPostQuery } = api

向查询添加元信息

baseQuery 也可以在其返回值中包含一个 meta 属性。这在您可能希望包含与请求相关的附加信息(例如请求 ID 或时间戳)的情况下很有用。

在这种情况下,返回值将如下所示

  1. 包含元信息的预期成功结果格式
    return { data: YourData, meta: YourMeta }
  2. 包含元信息的预期错误结果格式
    return { error: YourError, meta: YourMeta }
包含元信息的 baseQuery 示例
import { fetchBaseQuery, createApi } from '@reduxjs/toolkit/query'
import type {
BaseQueryFn,
FetchArgs,
FetchBaseQueryError,
} from '@reduxjs/toolkit/query'
import type { FetchBaseQueryMeta } from '@reduxjs/toolkit/dist/query/fetchBaseQuery'
import { uuid } from './idGenerator'

type Meta = {
requestId: string
timestamp: number
}

const metaBaseQuery: BaseQueryFn<
string | FetchArgs,
unknown,
FetchBaseQueryError,
{},
Meta & FetchBaseQueryMeta
> = async (args, api, extraOptions) => {
const requestId = uuid()
const timestamp = Date.now()

const baseResult = await fetchBaseQuery({ baseUrl: '/' })(
args,
api,
extraOptions
)

return {
...baseResult,
meta: baseResult.meta && { ...baseResult.meta, requestId, timestamp },
}
}

const DAY_MS = 24 * 60 * 60 * 1000

interface Post {
id: number
name: string
timestamp: number
}
type PostsResponse = Post[]

const api = createApi({
baseQuery: metaBaseQuery,
endpoints: (build) => ({
// a theoretical endpoint where we only want to return data
// if request was performed past a certain date
getRecentPosts: build.query<PostsResponse, void>({
query: () => 'posts',
transformResponse: (returnValue: PostsResponse, meta) => {
// `meta` here contains our added `requestId` & `timestamp`, as well as
// `request` & `response` from fetchBaseQuery's meta object.
// These properties can be used to transform the response as desired.
if (!meta) return []
return returnValue.filter(
(post) => post.timestamp >= meta.timestamp - DAY_MS
)
},
}),
}),
})

使用 Redux 状态构建动态基本 URL

在某些情况下,您可能希望从 Redux 状态中的属性确定动态更改的基本 URL。baseQuery 可以访问 getState 方法,该方法在调用时提供当前存储状态。这可用于使用部分 URL 字符串和存储状态中的适当数据来构建所需的 URL。

动态生成的 Base URL 示例
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type {
BaseQueryFn,
FetchArgs,
FetchBaseQueryError,
} from '@reduxjs/toolkit/query/react'
import type { Post } from './types'
import { selectProjectId } from './projectSlice'
import type { RootState } from '../store'

const rawBaseQuery = fetchBaseQuery({
baseUrl: 'www.my-cool-site.com/',
})

const dynamicBaseQuery: BaseQueryFn<
string | FetchArgs,
unknown,
FetchBaseQueryError
> = async (args, api, extraOptions) => {
const projectId = selectProjectId(api.getState() as RootState)
// gracefully handle scenarios where data to generate the URL is missing
if (!projectId) {
return {
error: {
status: 400,
statusText: 'Bad Request',
data: 'No project ID received',
},
}
}

const urlEnd = typeof args === 'string' ? args : args.url
// construct a dynamically generated portion of the url
const adjustedUrl = `project/${projectId}/${urlEnd}`
const adjustedArgs =
typeof args === 'string' ? adjustedUrl : { ...args, url: adjustedUrl }
// provide the amended url and other params to the raw base query
return rawBaseQuery(adjustedArgs, api, extraOptions)
}

export const api = createApi({
baseQuery: dynamicBaseQuery,
endpoints: (builder) => ({
getPosts: builder.query<Post[], void>({
query: () => 'posts',
}),
}),
})

export const { useGetPostsQuery } = api

/*
Using `useGetPostsQuery()` where a `projectId` of 500 is in the redux state will result in
a request being sent to www.my-cool-site.com/project/500/posts
*/

示例 - transformResponse

解压缩深度嵌套的 GraphQL 数据

GraphQL 转换示例
import { createApi } from '@reduxjs/toolkit/query'
import { graphqlBaseQuery, gql } from './graphqlBaseQuery'

interface Post {
id: number
title: string
}

export const api = createApi({
baseQuery: graphqlBaseQuery({
baseUrl: '/graphql',
}),
endpoints: (builder) => ({
getPosts: builder.query<Post[], void>({
query: () => ({
body: gql`
query {
posts {
data {
id
title
}
}
}
`,
}),
transformResponse: (response: { posts: { data: Post[] } }) =>
response.posts.data,
}),
}),
})

使用 createEntityAdapter 规范化数据

在下面的示例中,transformResponsecreateEntityAdapter 结合使用,在将数据存储到缓存之前对其进行规范化。

对于这样的响应

[
{ id: 1, name: 'Harry' },
{ id: 2, name: 'Ron' },
{ id: 3, name: 'Hermione' },
]

规范化的缓存数据将存储为

{
ids: [1, 3, 2],
entities: {
1: { id: 1, name: "Harry" },
2: { id: 2, name: "Ron" },
3: { id: 3, name: "Hermione" },
}
}
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { createEntityAdapter } from '@reduxjs/toolkit'
import type { EntityState } from '@reduxjs/toolkit'

export interface Post {
id: number
name: string
}

const postsAdapter = createEntityAdapter<Post>({
sortComparer: (a, b) => a.name.localeCompare(b.name),
})

export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getPosts: build.query<EntityState<Post, number>, void>({
query: () => `posts`,
transformResponse(response: Post[]) {
return postsAdapter.addMany(postsAdapter.getInitialState(), response)
},
}),
}),
})

export const { useGetPostsQuery } = api

示例 - queryFn

使用第三方 SDK

许多服务(如 Firebase 和 Supabase)提供自己的 SDK 来发出请求。您可以在 queryFn 中使用这些 SDK 方法

基本第三方 SDK
import { createApi, fakeBaseQuery } from '@reduxjs/toolkit/query/react'
import { supabase } from './supabaseApi'

export const supabaseApi = createApi({
reducerPath: 'supabaseApi',
baseQuery: fakeBaseQuery(),
endpoints: (builder) => ({
getBlogs: builder.query({
queryFn: async () => {
// Supabase conveniently already has `data` and `error` fields
const { data, error } = await supabase.from('blogs').select()
if (error) {
return { error }
}
return { data }
},
}),
}),
})

您也可以尝试创建一个使用 SDK 的自定义基本查询,并定义将方法名称或参数传递到该基本查询的端点。

使用无操作 queryFn

在某些情况下,您可能希望有一个 querymutation,其中发送请求或返回数据与当前情况无关。这种情况将利用 invalidatesTags 属性强制重新获取已提供给缓存的特定 tags

另请参阅 向缓存提供错误,以查看此类场景的更多详细信息和示例,以“重新获取出错的查询”。

使用无操作 queryFn
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'

const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => 'posts',
providesTags: ['Post'],
}),

getUsers: build.query<User[], void>({
query: () => 'users',
providesTags: ['User'],
}),

refetchPostsAndUsers: build.mutation<null, void>({
// The query is not relevant here, so a `null` returning `queryFn` is used
queryFn: () => ({ data: null }),
// This mutation takes advantage of tag invalidation behaviour to trigger
// any queries that provide the 'Post' or 'User' tags to re-fetch if the queries
// are currently subscribed to the cached data
invalidatesTags: ['Post', 'User'],
}),
}),
})

无初始请求的流式数据

RTK Query 提供了端点发送数据初始请求的功能,随后是重复的 流式更新,这些更新在更新发生时对缓存数据执行进一步更新。但是,初始请求是可选的,您可能希望在没有发出任何初始请求的情况下使用流式更新。

在下面的示例中,queryFn 用于使用空数组填充缓存数据,而不发送任何初始请求。该数组随后通过 onCacheEntryAdded 端点选项使用流式更新进行填充,在收到数据时更新缓存数据。

无初始请求的流式数据
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Message } from './types'

const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Message'],
endpoints: (build) => ({
streamMessages: build.query<Message[], void>({
// The query is not relevant here as the data will be provided via streaming updates.
// A queryFn returning an empty array is used, with contents being populated via
// streaming updates below as they are received.
queryFn: () => ({ data: [] }),
async onCacheEntryAdded(arg, { updateCachedData, cacheEntryRemoved }) {
const ws = new WebSocket('ws://localhost:8080')
// populate the array with messages as they are received from the websocket
ws.addEventListener('message', (event) => {
updateCachedData((draft) => {
draft.push(JSON.parse(event.data))
})
})
await cacheEntryRemoved
ws.close()
},
}),
}),
})

使用单个查询执行多个请求

在下面的示例中,编写了一个查询来获取随机用户的全部帖子。这是通过先请求一个随机用户,然后获取该用户的全部帖子来完成的。使用 queryFn 允许将两个请求包含在单个查询中,避免必须在组件代码中链接该逻辑。

使用单个查询执行多个请求
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { FetchBaseQueryError } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'

const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/ ' }),
endpoints: (build) => ({
getRandomUserPosts: build.query<Post, void>({
async queryFn(_arg, _queryApi, _extraOptions, fetchWithBQ) {
// get a random user
const randomResult = await fetchWithBQ('users/random')
if (randomResult.error)
return { error: randomResult.error as FetchBaseQueryError }
const user = randomResult.data as User
const result = await fetchWithBQ(`user/${user.id}/posts`)
return result.data
? { data: result.data as Post }
: { error: result.error as FetchBaseQueryError }
},
}),
}),
})