自動重新擷取
正如在 預設快取行為 中所見,當為查詢端點新增訂閱時,只有在快取資料不存在時才會傳送請求。如果它存在,則會提供現有的資料。
RTK Query 使用「快取標籤」系統,自動重新擷取資料會受到變更端點影響的查詢端點。這讓您可以設計 API,讓觸發特定變更會讓特定查詢端點視其快取資料為無效,並在有訂閱活動時重新擷取資料。
每個端點 + 參數組合會提供自己的 queryCacheKey
。快取標籤系統讓 RTK Query 得以得知特定查詢快取已提供特定標籤。如果觸發的變更宣稱會失效查詢快取已提供的標籤,快取資料會被視為已失效,並在有訂閱快取資料的活動時重新擷取。
如要透過其他方式觸發重新擷取,請參閱 操作快取行為。
定義
標籤
另請參閱:tagTypes API 參考
對於 RTK Query,標籤只是一個名稱,您可以將其提供給特定資料集合,以控制快取和失效行為,以便重新擷取。它可以視為附加到快取資料的「標籤」,在變更後讀取,以決定資料是否應受到變更影響。
在定義 API 時,標籤會定義在 tagTypes
引數中。例如,在同時有 Posts
和 Users
的應用程式中,您可以在呼叫 createApi
時定義 tagTypes: ['Post', 'User']
。
個別的 標籤
會有一個 類型
,表示為 字串
名稱,以及一個選用的 id
,表示為 字串
或 數字
。它可以表示為純文字字串(例如 'Post'
),或形狀為 {type: string, id?: string|number}
的物件(例如 [{type: 'Post', id: 1}]
)。
提供標籤
另請參閱:providesTags API 參考
查詢可以讓其快取資料提供標籤。這樣會決定附加到查詢傳回的快取資料的「標籤」。
providesTags
引數可以是 字串
陣列(例如 ['Post']
)、{type: string, id?: string|number}
(例如 [{type: 'Post', id: 1}]
),或傳回此類陣列的回呼。該函式會將結果傳遞為第一個引數、回應錯誤傳遞為第二個引數,並將最初傳遞給 query
方法的引數傳遞為第三個引數。請注意,結果或錯誤引數可能會是未定義的,具體取決於查詢是否成功。
失效標籤
變異可以根據標籤使特定快取資料失效。這麼做會決定哪些快取資料將重新擷取或從快取中移除。
invalidatesTags
參數可以是 string
陣列(例如 ['Post']
)、{type: string, id?: string|number}
(例如 [{type: 'Post', id: 1}]
),或傳回此類陣列的回呼函式。該函式會將結果作為第一個參數傳遞,將回應錯誤作為第二個參數傳遞,並將最初傳遞給 query
方法的參數作為第三個參數傳遞。請注意,結果或錯誤參數可能會未定義,具體取決於變異是否成功。
快取標籤
RTK Query 使用「標籤」的概念來判斷一個端點的變異是否打算使另一個端點的查詢提供的某些資料失效。
如果快取資料失效,它將重新擷取提供查詢(如果元件仍在使用該資料)或從快取中移除資料。
定義 API 區段時,createApi
接受標籤類型名稱陣列作為 tagTypes
屬性,這是 API 區段查詢可能提供的可能的標籤名稱選項清單。
以下範例宣告端點可能提供「文章」和/或「使用者」至快取
- TypeScript
- JavaScript
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',
}),
getUsers: build.query<User[], void>({
query: () => '/users',
}),
addPost: build.mutation<Post, Omit<Post, 'id'>>({
query: (body) => ({
url: 'post',
method: 'POST',
body,
}),
}),
editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: '/',
}),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query({
query: () => '/posts',
}),
getUsers: build.query({
query: () => '/users',
}),
addPost: build.mutation({
query: (body) => ({
url: 'post',
method: 'POST',
body,
}),
}),
editPost: build.mutation({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
}),
}),
})
透過宣告這些標籤為可能提供至快取的內容,可以控制個別變異端點是否宣稱影響快取的特定部分,並搭配個別端點上的 providesTags
和 invalidatesTags
。
提供快取資料
每個個別 query
端點的快取資料都可以提供特定標籤。這麼做會建立一個或多個查詢端點的快取資料與一個或多個變異端點的行為之間的關係。
query
端點上的 providesTags
屬性用於此目的。
提供的標籤在不同的 query
端點之間沒有內在關係。提供的標籤用於判斷端點傳回的快取資料是否應失效,並重新擷取或從快取中移除。如果兩個不同的端點提供相同的標籤,它們仍會提供自己的不同快取資料,稍後可以透過變異宣告的單一標籤使它們同時失效。
以下範例宣告 getPosts
query
端點使用 query
端點的 providesTags
屬性,將 'Post'
標籤提供給快取。
- TypeScript
- JavaScript
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'],
}),
addPost: build.mutation<Post, Omit<Post, 'id'>>({
query: (body) => ({
url: 'posts',
method: 'POST',
body,
}),
}),
editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: '/',
}),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query({
query: () => '/posts',
providesTags: ['Post'],
}),
getUsers: build.query({
query: () => '/users',
providesTags: ['User'],
}),
addPost: build.mutation({
query: (body) => ({
url: 'posts',
method: 'POST',
body,
}),
}),
editPost: build.mutation({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
}),
}),
})
若要更精細地控制提供的資料,提供的 標籤
可以有相關聯的 id
。這能區分「任何特定標籤類型」和「特定標籤類型的特定實例」。
以下範例宣告提供的文章與特定 ID 相關聯,由端點傳回的結果決定
- TypeScript
- JavaScript
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: (result, error, arg) =>
result
? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']
: ['Post'],
}),
getUsers: build.query<User[], void>({
query: () => '/users',
providesTags: ['User'],
}),
addPost: build.mutation<Post, Omit<Post, 'id'>>({
query: (body) => ({
url: 'post',
method: 'POST',
body,
}),
}),
editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: '/',
}),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query({
query: () => '/posts',
providesTags: (result, error, arg) =>
result
? [...result.map(({ id }) => ({ type: 'Post', id })), 'Post']
: ['Post'],
}),
getUsers: build.query({
query: () => '/users',
providesTags: ['User'],
}),
addPost: build.mutation({
query: (body) => ({
url: 'post',
method: 'POST',
body,
}),
}),
editPost: build.mutation({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
}),
}),
})
請注意,對於上述範例,在成功結果中會盡可能使用 id
。如果發生錯誤,則不會提供結果,而且我們仍會視為它已提供一般 'Post'
標籤類型,而非該標籤的任何特定實例。
若要更強而有力地控制失效適當的資料,您可以使用任意 ID,例如特定標籤的 'LIST'
。請參閱 使用抽象標籤 ID 的進階失效 以取得更多詳細資料。
失效快取資料
每個個別的變異端點可以 失效
現有快取資料的特定標籤。這麼做能建立一個或多個查詢端點的快取資料與一個或多個變異端點的行為之間的關聯性。
變異端點上的 invalidatesTags
屬性用於此目的。
以下範例宣告 addPost
和 editPost
變異端點 失效
任何帶有 'Post'
標籤的快取資料,使用變異端點的 invalidatesTags
屬性
- TypeScript
- JavaScript
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: (result, error, arg) =>
result
? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']
: ['Post'],
}),
getUsers: build.query<User[], void>({
query: () => '/users',
providesTags: ['User'],
}),
addPost: build.mutation<Post, Omit<Post, 'id'>>({
query: (body) => ({
url: 'post',
method: 'POST',
body,
}),
invalidatesTags: ['Post'],
}),
editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
invalidatesTags: ['Post'],
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: '/',
}),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query({
query: () => '/posts',
providesTags: (result, error, arg) =>
result
? [...result.map(({ id }) => ({ type: 'Post', id })), 'Post']
: ['Post'],
}),
getUsers: build.query({
query: () => '/users',
providesTags: ['User'],
}),
addPost: build.mutation({
query: (body) => ({
url: 'post',
method: 'POST',
body,
}),
invalidatesTags: ['Post'],
}),
editPost: build.mutation({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
invalidatesTags: ['Post'],
}),
}),
})
對於上述範例,這會告訴 RTK Query 在呼叫並完成 addPost
和/或 editPost
變更後,任何提供 'Post'
標籤的快取資料都將不再有效。如果在呼叫並完成上述變更後,某個元件目前訂閱 'Post'
標籤的快取資料,它會自動重新擷取以從伺服器取得最新資料。
範例情境如下
- 某個元件已呈現,它使用
useGetPostsQuery()
勾子訂閱該端點的快取資料 - 發出
/posts
要求,伺服器回應 ID 為 1、2 和 3 的文章 getPosts
端點將接收到的資料儲存在快取中,並在內部註冊已提供的下列標籤[
{ type: 'Post', id: 1 },
{ type: 'Post', id: 2 },
{ type: 'Post', id: 3 },
]- 發出
editPost
變更以修改特定文章 - 完成後,RTK Query 在內部註冊
'Post'
標籤現在已失效,並從快取中移除先前提供的'Post'
標籤 - 由於
getPosts
端點已提供類型為'Post'
的標籤,而此標籤現在具有無效的快取資料,且元件仍訂閱該資料,因此/posts
要求會自動再次發出,擷取新資料並為更新的快取資料註冊新標籤
若要更精細地控制失效的資料,失效的 標籤
可以像 providesTags
一樣具有關聯的 id
。這使得可以區分「特定標籤類型的任何一個」和「特定標籤類型的特定實例」。
以下範例宣告 editPost
變更會使 Post
標籤的特定實例失效,使用在呼叫變更函式時傳入的 ID
- TypeScript
- JavaScript
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: (result, error, arg) =>
result
? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']
: ['Post'],
}),
getUsers: build.query<User[], void>({
query: () => '/users',
providesTags: ['User'],
}),
addPost: build.mutation<Post, Omit<Post, 'id'>>({
query: (body) => ({
url: 'post',
method: 'POST',
body,
}),
invalidatesTags: ['Post'],
}),
editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
invalidatesTags: (result, error, arg) => [{ type: 'Post', id: arg.id }],
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: '/',
}),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query({
query: () => '/posts',
providesTags: (result, error, arg) =>
result
? [...result.map(({ id }) => ({ type: 'Post', id })), 'Post']
: ['Post'],
}),
getUsers: build.query({
query: () => '/users',
providesTags: ['User'],
}),
addPost: build.mutation({
query: (body) => ({
url: 'post',
method: 'POST',
body,
}),
invalidatesTags: ['Post'],
}),
editPost: build.mutation({
query: (body) => ({
url: `post/${body.id}`,
method: 'POST',
body,
}),
invalidatesTags: (result, error, arg) => [{ type: 'Post', id: arg.id }],
}),
}),
})
對於上述範例,呼叫 editPost
變更函式現在只會使提供的 id
的標籤失效,而不是使類型為 'Post'
的任何標籤失效。亦即,如果端點的快取資料未提供該 id
的 'Post'
,它仍會被視為「有效」,且不會觸發自動重新擷取。
若要更強而有力地控制失效適當的資料,您可以使用任意 ID,例如特定標籤的 'LIST'
。請參閱 使用抽象標籤 ID 的進階失效 以取得更多詳細資料。
標籤失效行為
下表顯示範例,說明哪些失效標籤會影響並使哪些提供的標籤失效
提供的 失效的 | 一般標籤 A ['Post'] / [{ type: 'Post' }] | 一般標籤 B ['User'] / [{ type: 'User' }] | 特定標籤 A1 [{ type: 'Post', id: 1 }] | 特定標籤 A2 [{ type: 'Post', id: 'LIST' }] | 特定標籤 B1 [{ type: 'User', id: 1 }] | 特定標籤 B2 [{ type: 'User', id: 2 }] |
---|---|---|---|---|---|---|
一般標籤 A ['Post'] / [{ type: 'Post' }] | ✔️ | ✔️ | ✔️ | |||
一般標籤 B ['User'] / [{ type: 'User' }] | ✔️ | ✔️ | ✔️ | |||
特定標籤 A1 [{ type: 'Post', id: 1 }] | ✔️ | |||||
特定標籤 A2 [{ type: 'Post', id: 'LIST' }] | ✔️ | |||||
特定標籤 B1 [{ type: 'User', id: 1 }] | ✔️ | |||||
特定標籤 B2 [{ type: 'User', id: 2 }] | ✔️ |
失效行為根據以下各節中的標籤特定性進行摘要。
一般標籤
例如 ['Post'] / [{ type: 'Post' }]
將失效
任何與匹配類型相符的提供的
標籤,包括一般標籤和特定標籤。
範例
如果 Post
的一般標籤失效,則其資料提供
下列標籤的端點將全部失效其資料
['Post']
[{ type: 'Post' }]
[{ type: 'Post' }, { type: 'Post', id: 1 }]
[{ type: 'Post', id: 1 }]
[{ type: 'Post', id: 1 }, { type: 'User' }]
[{ type: 'Post', id: 'LIST' }]
[{ type: 'Post', id: 1 }, { type: 'Post', id: 'LIST' }]
其資料提供
下列標籤的端點將不會失效其資料
['User']
[{ type: 'User' }]
[{ type: 'User', id: 1 }]
[{ type: 'User', id: 'LIST' }]
[{ type: 'User', id: 1 }, { type: 'User', id: 'LIST' }]
特定標籤
例如 [{ type: 'Post', id: 1 }]
將失效
任何與匹配類型和匹配 ID 相符的提供的
標籤。不會直接導致一般
標籤失效,但可能會使提供一般
標籤的端點資料失效,如果它也提供匹配的特定
標籤。
範例 1:如果 { type: 'Post', id: 1 }
的特定標籤失效,則其資料提供
下列標籤的端點將全部失效其資料
[{ type: 'Post' }, { type: 'Post', id: 1 }]
[{ type: 'Post', id: 1 }]
[{ type: 'Post', id: 1 }, { type: 'User' }]
[{ type: 'Post', id: 1 }, { type: 'Post', id: 'LIST' }]
其資料提供
下列標籤的端點將不會失效其資料
['Post']
[{ type: 'Post' }]
[{ type: 'Post', id: 'LIST' }]
['User']
[{ type: 'User' }]
[{ type: 'User', id: 1 }]
[{ type: 'User', id: 'LIST' }]
[{ type: 'User', id: 1 }, { type: 'User', id: 'LIST' }]
範例 2:如果 { type: 'Post', id: 'LIST' }
的特定標籤失效,則其資料提供
下列標籤的端點將全部失效其資料
[{ type: 'Post', id: 'LIST' }]
[{ type: 'Post', id: 1 }, { type: 'Post', id: 'LIST' }]
其資料提供
下列標籤的端點將不會失效其資料
['Post']
[{ type: 'Post' }]
[{ type: 'Post' }, { type: 'Post', id: 1 }]
[{ type: 'Post', id: 1 }]
[{ type: 'Post', id: 1 }, { type: 'User' }]
['User']
[{ type: 'User' }]
[{ type: 'User', id: 1 }]
[{ type: 'User', id: 'LIST' }]
[{ type: 'User', id: 1 }, { type: 'User', id: 'LIST' }]
食譜
進階無效化與抽象標籤 ID
雖然將「實體 ID」用於標籤 id
是常見的用例,但 id
屬性並非僅限於資料庫 ID。id
只是標記特定 標籤類型
資料子集的一種方式。
強大的用例是將 ID 如 'LIST'
用作大量查詢提供的資料標籤,以及將實體 ID 用於個別項目。這樣做可讓未來的 mutations
宣告它們是否僅在資料包含特定項目(例如 { type: 'Post', id: 5 }
)時使資料無效,或是在資料為 'LIST'
時使資料無效(例如 { type: 'Post', id: 'LIST' }
)。
LIST
是任意字串 - 從技術上來說,您可以在此處使用任何您想要的內容,例如ALL
或*
。選擇自訂 ID 時,重要的是確保它不會與查詢結果傳回的 ID 發生衝突。如果您在查詢結果中具有未知 ID 且不希望冒險,您可以執行以下第 3 點。- 您可以新增多個標籤類型以獲得更多控制
[{ type: 'Posts', id: 'LIST' }, { type: 'Posts', id: 'SVELTE_POSTS' }, { type: 'Posts', id: 'REACT_POSTS' }]
- 如果您覺得使用 'LIST' 等
id
的概念很奇怪,您隨時可以新增另一個tagType
並使它的根無效,但我們建議使用如所示的id
方法。
我們可以比較以下場景,以了解如何使用 'LIST'
id 來最佳化行為。
使某類型的所有內容無效
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Post, User } from './types'
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => 'posts',
providesTags: (result) =>
result ? result.map(({ id }) => ({ type: 'Posts', id })) : ['Posts'],
}),
addPost: build.mutation<Post, Partial<Post>>({
query: (body) => ({
url: `post`,
method: 'POST',
body,
}),
invalidatesTags: ['Posts'],
}),
getPost: build.query<Post, number>({
query: (id) => `post/${id}`,
providesTags: (result, error, id) => [{ type: 'Posts', id }],
}),
}),
})
export const { useGetPostsQuery, useGetPostQuery, useAddPostMutation } = api
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query({
query: () => 'posts',
providesTags: (result) =>
result ? result.map(({ id }) => ({ type: 'Posts', id })) : ['Posts'],
}),
addPost: build.mutation({
query: (body) => ({
url: `post`,
method: 'POST',
body,
}),
invalidatesTags: ['Posts'],
}),
getPost: build.query({
query: (id) => `post/${id}`,
providesTags: (result, error, id) => [{ type: 'Posts', id }],
}),
}),
})
export const { useGetPostsQuery, useGetPostQuery, useAddPostMutation } = api
function App() {
const { data: posts } = useGetPostsQuery()
const [addPost] = useAddPostMutation()
return (
<div>
<AddPost onAdd={addPost} />
<PostsList />
{/* Assume each PostDetail is subscribed via `const {data} = useGetPostQuery(id)` */}
<PostDetail id={1} />
<PostDetail id={2} />
<PostDetail id={3} />
</div>
)
}
預期結果
當觸發 addPost
時,會導致每個 PostDetail
元件回到 isFetching
狀態,因為 addPost
會使根標籤失效,這會導致提供「貼文」的每個查詢重新執行。在多數情況下,這可能不是你想要的。想像一下,如果你在螢幕上有 100 篇貼文,全部都訂閱 getPost
查詢,在這種情況下,你會建立 100 個要求,並向你的伺服器傳送大量不必要的流量,而這正是我們一開始就試圖避免的!即使使用者仍然會看到最後一個好的快取結果,而且可能不會注意到任何事情,除了瀏覽器打嗝之外,你仍然希望避免這種情況。
選擇性地使清單失效
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Post, User } from './types'
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
tagTypes: ['Posts'],
endpoints: (build) => ({
getPosts: build.query<Post[], 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: `post`,
method: 'POST',
body,
}
},
invalidatesTags: [{ type: 'Posts', id: 'LIST' }],
}),
getPost: build.query<Post, number>({
query: (id) => `post/${id}`,
providesTags: (result, error, id) => [{ type: 'Posts', id }],
}),
}),
})
export const { useGetPostsQuery, useAddPostMutation, useGetPostQuery } = api
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
export 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: `post`,
method: 'POST',
body,
}
},
invalidatesTags: [{ type: 'Posts', id: 'LIST' }],
}),
getPost: build.query({
query: (id) => `post/${id}`,
providesTags: (result, error, id) => [{ type: 'Posts', id }],
}),
}),
})
export const { useGetPostsQuery, useAddPostMutation, useGetPostQuery } = api
function App() {
const { data: posts } = useGetPostsQuery()
const [addPost] = useAddPostMutation()
return (
<div>
<AddPost onAdd={addPost} />
<PostsList />
{/* Assume each PostDetail is subscribed via `const {data} = useGetPostQuery(id)` */}
<PostDetail id={1} />
<PostDetail id={2} />
<PostDetail id={3} />
</div>
)
}
預期結果
當觸發 addPost
時,它只會導致 PostsList
進入 isFetching
狀態,因為 addPost
只會使 'LIST'
ID 失效,這會導致 getPosts
重新執行(因為它提供了那個特定 ID)。因此,在你的網路標籤中,你只會看到 1 個新的 GET /posts
要求觸發。由於單數 getPost
查詢尚未失效,因此它們不會因為 addPost
而重新執行。
如果你打算讓 addPost
變動更新所有貼文,包括個別 PostDetail
元件,同時仍然只建立 1 個新的 GET /posts
要求,這可以使用 selectFromResult
選擇資料的一部分來完成。
提供錯誤給快取
提供給快取的資訊不限於成功的資料擷取。這個概念可以用來通知 RTK Query,當遇到特定失敗時,為該失敗快取資料提供特定 標籤
。然後,另一個端點可以為該 標籤
的資料 失效
,告訴 RTK Query 重新嘗試先前失敗的端點,如果元件仍然訂閱失敗的資料。
以下範例示範了具有下列行為的範例
- 如果查詢因錯誤代碼
401 UNAUTHORIZED
而失敗,則提供UNAUTHORIZED
快取標籤 - 如果查詢因其他錯誤而失敗,則提供
UNKNOWN_ERROR
快取標籤 - 啟用「登入」變動,當成功時,將
失效
標籤為UNAUTHORIZED
的資料。
如果postById
的最後一次呼叫遇到未授權錯誤,而且- 元件仍然訂閱快取資料
- 這將觸發
postById
端點重新觸發
如果- 最後呼叫
postById
時遇到未知錯誤,而且 - 元件仍然訂閱快取資料
- 最後呼叫
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import { Post, LoginResponse } from './types'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
tagTypes: ['Post', 'UNAUTHORIZED', 'UNKNOWN_ERROR'],
endpoints: (build) => ({
postById: build.query<Post, number>({
query: (id) => `post/${id}`,
providesTags: (result, error, id) =>
result
? [{ type: 'Post', id }]
: error?.status === 401
? ['UNAUTHORIZED']
: ['UNKNOWN_ERROR'],
}),
login: build.mutation<LoginResponse, void>({
query: () => '/login',
// on successful login, will refetch all currently
// 'UNAUTHORIZED' queries
invalidatesTags: (result) => (result ? ['UNAUTHORIZED'] : []),
}),
refetchErroredQueries: build.mutation<null, void>({
queryFn: () => ({ data: null }),
invalidatesTags: ['UNKNOWN_ERROR'],
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
tagTypes: ['Post', 'UNAUTHORIZED', 'UNKNOWN_ERROR'],
endpoints: (build) => ({
postById: build.query({
query: (id) => `post/${id}`,
providesTags: (result, error, id) =>
result
? [{ type: 'Post', id }]
: error?.status === 401
? ['UNAUTHORIZED']
: ['UNKNOWN_ERROR'],
}),
login: build.mutation({
query: () => '/login',
// on successful login, will refetch all currently
// 'UNAUTHORIZED' queries
invalidatesTags: (result) => (result ? ['UNAUTHORIZED'] : []),
}),
refetchErroredQueries: build.mutation({
queryFn: () => ({ data: null }),
invalidatesTags: ['UNKNOWN_ERROR'],
}),
}),
})
抽象化共用的提供/失效用法
為特定 API 區段撰寫的 provide
和 invalidate
標籤程式碼會取決於多項因素,包括
- 後端傳回的資料形狀
- 預期特定查詢端點提供的標籤
- 預期特定變異端點失效的標籤
- 希望使用失效功能的程度
在宣告 API 區段時,你可能會覺得自己在複製程式碼。例如,對於兩個都提供特定實體清單的兩個獨立端點,providesTags
宣告可能只在提供的 tagType
中有所不同。
例如
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query<Post[], void>({
query: () => `posts`,
providesTags: (result) =>
result
? [
{ type: 'Post', id: 'LIST' },
...result.map(({ id }) => ({ type: 'Post' as const, id })),
]
: [{ type: 'Post', id: 'LIST' }],
}),
getUsers: build.query<User[], void>({
query: () => `users`,
providesTags: (result) =>
result
? [
{ type: 'User', id: 'LIST' },
...result.map(({ id }) => ({ type: 'User' as const, id })),
]
: [{ type: 'User', id: 'LIST' }],
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query({
query: () => `posts`,
providesTags: (result) =>
result
? [
{ type: 'Post', id: 'LIST' },
...result.map(({ id }) => ({ type: 'Post', id })),
]
: [{ type: 'Post', id: 'LIST' }],
}),
getUsers: build.query({
query: () => `users`,
providesTags: (result) =>
result
? [
{ type: 'User', id: 'LIST' },
...result.map(({ id }) => ({ type: 'User', id })),
]
: [{ type: 'User', id: 'LIST' }],
}),
}),
})
你可能會發現定義針對特定 API 設計的輔助函式很有用,以減少端點定義中的樣板,例如
- TypeScript
- JavaScript
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'
function providesList<R extends { id: string | number }[], T extends string>(
resultsWithIds: R | undefined,
tagType: T
) {
return resultsWithIds
? [
{ type: tagType, id: 'LIST' },
...resultsWithIds.map(({ id }) => ({ type: tagType, id })),
]
: [{ type: tagType, id: 'LIST' }]
}
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query({
query: () => `posts`,
providesTags: (result) => providesList(result, 'Post'),
}),
getUsers: build.query({
query: () => `users`,
providesTags: (result) => providesList(result, 'User'),
}),
}),
})
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
function providesList(resultsWithIds, tagType) {
return resultsWithIds
? [
{ type: tagType, id: 'LIST' },
...resultsWithIds.map(({ id }) => ({ type: tagType, id })),
]
: [{ type: tagType, id: 'LIST' }]
}
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
tagTypes: ['Post', 'User'],
endpoints: (build) => ({
getPosts: build.query({
query: () => `posts`,
providesTags: (result) => providesList(result, 'Post'),
}),
getUsers: build.query({
query: () => `users`,
providesTags: (result) => providesList(result, 'User'),
}),
}),
})
可以在以下 gist 中看到針對常見的 rest 資料格式設計的各種標籤提供/失效抽象化範例,包括 typescript 支援,以及考量 「清單」樣式的進階標籤失效 和 「錯誤」樣式的標籤失效:RTK Query 快取公用程式。