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 Chat 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 屬性的物件,或會解析為傳回此類物件的承諾。

基本 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 未授權 錯誤時,會傳送額外的要求嘗試更新授權令牌,並在重新授權後重新嘗試初始查詢。

使用自訂基本查詢模擬類 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 未授權 錯誤而失敗時,多個呼叫至 '/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

資訊

掛鉤有可能同時傳回 資料錯誤。預設情況下,RTK Query 會保留 資料 中最後一個「良好」的結果,直到它可以更新或被垃圾回收為止。

放棄錯誤重試

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. 預期成功結果格式(含 meta 資料)
    return { data: YourData, meta: YourMeta }
  2. 預期錯誤結果格式(含 meta 資料)
    return { error: YourError, meta: YourMeta }
包含 meta 資訊的 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 狀態建構動態基本網址

在某些情況下,您可能希望根據 Redux 狀態中的屬性來動態變更基本網址。baseQuery 可以存取 getState 方法,該方法會在呼叫時提供目前的儲存體狀態。這可以用於使用部分網址字串和儲存體狀態中的適當資料來建構所需的網址。

動態產生的基本網址範例
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 的自訂基本查詢,並定義將方法名稱或參數傳遞到該基本查詢的端點。

使用 no-op queryFn

在某些情況下,您可能希望有一個 querymutation,其中傳送請求或傳回資料與情況無關。此類情況將利用 invalidatesTags 屬性來強制重新擷取已提供給快取的特定 tags

請參閱 提供錯誤給快取,以查看此類情況的更多詳細資訊和範例,以「重新擷取有錯誤的查詢」。

使用 no-op 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://127.0.0.1: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 }
},
}),
}),
})