メインコンテンツにスキップ

自動再フェッチ

デフォルトのキャッシュ動作で説明されているように、クエリのエンドポイントに対してサブスクリプションが追加された場合、キャッシュデータがまだ存在しない場合にのみリクエストが送信されます。存在する場合は、既存のデータが代わりに提供されます。

RTK Queryは、ミューテーションエンドポイントによってデータが影響を受けるクエリエンドポイントの再フェッチを自動化するために、「キャッシュタグ」システムを使用します。これにより、特定のミューテーションを発生させると、特定のクエリエンドポイントでキャッシュされたデータが無効と見なされ、アクティブなサブスクリプションがある場合はデータを再フェッチするようにAPIを設計できます。

各エンドポイント+パラメーターの組み合わせは、独自のqueryCacheKeyを提供します。キャッシュタグシステムにより、特定のクエリキャッシュが特定のタグを提供したことをRTK Queryに通知することができます。クエリキャッシュが提供したタグをinvalidateするミューテーションが実行された場合、キャッシュされたデータは無効化されたと見なされ、キャッシュされたデータへのアクティブなサブスクリプションがある場合は再フェッチされます。

他の手段で再フェッチをトリガーする方法については、キャッシュ動作の操作を参照してください。

定義

タグ

参照:tagTypes APIリファレンス

RTK Queryの場合、タグは、再フェッチの目的でキャッシュと無効化の動作を制御するために、特定のデータコレクションに付けることができる単なる名前です。これは、ミューテーション後に読み取られる、キャッシュされたデータにアタッチされた「ラベル」と考えることができ、データがミューテーションの影響を受けるかどうかを決定します。

タグは、APIを定義するときにtagTypes引数で定義されます。たとえば、PostsUsersの両方を持つアプリケーションでは、createApiを呼び出すときにtagTypes: ['Post', 'User']を定義する場合があります。

個々のtagには、string名で表されるtypeと、オプションのstringまたはnumberで表されるidがあります。プレーンな文字列('Post'など)、または{type: string, id?: string|number}[{type: 'Post', id: 1}]など)の形式のオブジェクトで表すことができます。

タグの提供

参照:providesTags APIリファレンス

クエリは、キャッシュされたデータにタグを提供させることができます。これにより、クエリによって返されるキャッシュされたデータにアタッチされる「タグ」が決まります。

providesTags引数は、stringの配列(['Post']など)、{type: string, id?: string|number}[{type: 'Post', id: 1}]など)、またはそのような配列を返すコールバックのいずれかです。この関数には、最初引数として結果、2番目の引数として応答エラー、3番目の引数として最初にqueryメソッドに渡された引数が渡されます。クエリが成功したかどうかに応じて、結果またはエラー引数が未定義になる場合があることに注意してください。

タグの無効化

参照:invalidatesTags APIリファレンス

ミューテーションは、タグに基づいて特定のキャッシュされたデータを無効化できます。これにより、どのキャッシュデータが再フェッチされるか、またはキャッシュから削除されるかが決定されます。

invalidatesTags引数は、stringの配列(['Post']など)、{type: string, id?: string|number}[{type: 'Post', id: 1}]など)、またはそのような配列を返すコールバックのいずれかです。この関数には、最初引数として結果、2番目の引数として応答エラー、3番目の引数として最初にqueryメソッドに渡された引数が渡されます。ミューテーションが成功したかどうかに応じて、結果またはエラー引数が未定義になる場合があることに注意してください。

キャッシュタグ

RTK Queryは、あるエンドポイントのミューテーションが、別のエンドポイントからのクエリによって提供されたデータを無効化することを意図しているかどうかを判断するために、「タグ」の概念を使用します。

キャッシュデータが無効化されている場合、提供元のクエリを再フェッチするか(コンポーネントがまだそのデータを使用している場合)、データをキャッシュから削除します。

APIスライスを定義するとき、createApitagTypesプロパティのタグタイプ名の配列を受け入れます。これは、APIスライスのクエリが提供できる可能性のあるタグ名オプションのリストです。

以下の例では、エンドポイントがキャッシュに「Posts」および/または「Users」を提供する可能性があることを宣言しています。

キャッシュタグの宣言の例
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,
}),
}),
}),
})

これらのタグをキャッシュに提供できるものとして宣言することにより、個々のミューテーションエンドポイントが、個々のエンドポイントのprovidesTagsおよびinvalidatesTagsと組み合わせて、キャッシュの特定の部分に影響を与えるかどうかを主張するための制御が可能になります。

キャッシュデータの提供

個々のqueryエンドポイントは、キャッシュされたデータに特定のタグを提供させることができます。これにより、1つ以上のクエリエンドポイントからのキャッシュされたデータと、1つ以上のミューテーションエンドポイントの動作との関係が可能になります。

queryエンドポイントのprovidesTagsプロパティがこの目的に使用されます。

情報

提供されるタグは、個別のqueryエンドポイント間で固有の関係を持ちません。提供されるタグは、エンドポイントによって返されるキャッシュされたデータがinvalidatedされるべきかどうか、および再フェッチされるかキャッシュから削除されるかを判断するために使用されます。2つの別々のエンドポイントが同じタグを提供する場合でも、それらは独自の別個のキャッシュされたデータを提供し、後でミューテーションから宣言された単一のタグによって両方を無効化することができます。

以下の例では、getPostsqueryエンドポイントが、queryエンドポイントのprovidesTagsプロパティを使用して、キャッシュに'Post'タグをprovidesすることを宣言しています。

キャッシュにタグを提供する例
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,
}),
}),
}),
})

提供されたデータをより細かく制御するために、提供されたtagsは関連付けられたidを持つことができます。これにより、「特定のタグタイプのいずれか」と「特定のタグタイプの特定のインスタンス」を区別できます。

以下の例では、提供された投稿は、エンドポイントによって返された結果によって決定される特定のIDに関連付けられていることを宣言しています

キャッシュにID付きのタグを提供する例
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,
}),
}),
}),
})

上記の例では、成功した結果では可能な限りidが使用されていることに注意してください。エラーが発生した場合、結果は提供されず、特定のタグのインスタンスではなく、一般的な'Post'タグタイプを提供したと見なされます。

高度なリスト無効化

適切なデータの無効化をより強力に制御するために、特定のタグに対して'LIST'などの任意のIDを使用できます。詳細については、抽象タグIDによる高度な無効化を参照してください。

キャッシュデータの無効化

個々のミューテーションエンドポイントは、既存のキャッシュデータの特定のタグをinvalidateできます。これにより、1つ以上のクエリエンドポイントからのキャッシュされたデータと、1つ以上のミューテーションエンドポイントの動作との関係が可能になります。

ミューテーションエンドポイントのinvalidatesTagsプロパティがこの目的に使用されます。

以下の例では、addPostおよびeditPostミューテーションエンドポイントが、ミューテーションエンドポイントのinvalidatesTagsプロパティを使用して、'Post'タグを持つキャッシュされたデータをinvalidateすることを宣言しています。

キャッシュ内のタグを無効化する例
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'],
}),
}),
})

上記の例では、addPostおよび/またはeditPostミューテーションが呼び出されて完了した後、'Post'タグが付けられたキャッシュデータは有効ではなくなることをRTK Queryに通知します。上記のミューテーションが呼び出されて完了した後、コンポーネントが現在'Post'タグのキャッシュされたデータにサブスクライブしている場合、サーバーから最新のデータを取得するために自動的に再フェッチされます。

シナリオの例は次のようになります

  1. useGetPostsQuery()フックを使用して、そのエンドポイントのキャッシュされたデータにサブスクライブするコンポーネントがレンダリングされます
  2. /postsリクエストが開始され、サーバーがID 1、2、および3の投稿で応答します
  3. getPostsエンドポイントは、受信したデータをキャッシュに保存し、次のタグが提供されたことを内部的に登録します
    [
    { type: 'Post', id: 1 },
    { type: 'Post', id: 2 },
    { type: 'Post', id: 3 },
    ]
  4. 特定の投稿を変更するためにeditPostミューテーションが開始されます
  5. 完了すると、RTK Queryは内部的に'Post'タグが無効になったことを登録し、以前に提供された'Post'タグをキャッシュから削除します。
  6. getPostsエンドポイントが'Post'型のタグを提供しており、そのキャッシュデータが無効になったため、コンポーネントがまだデータにサブスクライブしている場合、/postsリクエストが自動的に再度発行され、新しいデータを取得し、更新されたキャッシュデータのために新しいタグを登録します。

無効化されたデータをより細かく制御するために、無効化されたtagsprovidesTagsと同じように関連付けられたidを持つことができます。これにより、「特定のタグタイプのいずれか」と「特定のタグタイプの特定のインスタンス」を区別できます。

以下の例では、editPostミューテーションがPostタグの特定のインスタンスを無効にすることを宣言しています。ミューテーション関数を呼び出すときに渡されるIDを使用します。

IDを使用してキャッシュを無効化するタグの例
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 }],
}),
}),
})

上記の例では、タイプが'Post'のタグを無効化するのではなく、editPostミューテーション関数を呼び出すと、提供されたidのタグのみが無効になります。つまり、エンドポイントからのキャッシュデータが同じid'Post'を提供しない場合、それは「有効」とみなされ、自動的に再フェッチされることはありません。

抽象的なタグIDの使用

適切なデータの無効化をより強力に制御するために、特定のタグに対して'LIST'などの任意のIDを使用できます。詳細については、抽象タグ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' }]

一致するタイプを持つ、一般的および特定のタグを含む、すべてのprovidedタグをinvalidateします。


Postの一般的なタグが無効化された場合、次のタグをprovidedしたデータを持つエンドポイントはすべて、そのデータが無効化されます。

  • ['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' }]

次のタグをprovidedしたデータを持つエンドポイントは、データが無効化されません。

  • ['User']
  • [{ type: 'User' }]
  • [{ type: 'User', id: 1 }]
  • [{ type: 'User', id: 'LIST' }]
  • [{ type: 'User', id: 1 }, { type: 'User', id: 'LIST' }]

特定のタグ

例:[{ type: 'Post', id: 1 }]

一致するタイプと一致するIDの両方を持つprovidedタグをinvalidateします。generalタグを直接無効にすることはありませんが、generalタグも提供する場合、一致するspecificタグを提供するエンドポイントのデータを無効にする可能性があります。

例1:{ type: 'Post', id: 1 }の特定のタグが無効化された場合、次のタグをprovidedしたデータを持つエンドポイントはすべて、そのデータが無効化されます。

  • [{ type: 'Post' }, { type: 'Post', id: 1 }]
  • [{ type: 'Post', id: 1 }]
  • [{ type: 'Post', id: 1 }, { type: 'User' }]
  • [{ type: 'Post', id: 1 }, { type: 'Post', id: 'LIST' }]

次のタグをprovidedしたデータを持つエンドポイントは、データが無効化されません。

  • ['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' }の特定のタグが無効化された場合、次のタグをprovidedしたデータを持つエンドポイントはすべて、そのデータが無効化されます。

  • [{ type: 'Post', id: 'LIST' }]
  • [{ type: 'Post', id: 1 }, { type: 'Post', id: 'LIST' }]

次のタグをprovidedしたデータを持つエンドポイントは、データが無効化されません。

  • ['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は、特定のtag typeの特定のデータコレクションのサブセットにラベルを付けるための単なる方法です。

強力なユースケースは、バルククエリによって提供されるデータのラベルとして'LIST'のようなIDを使用することです。また、個々のアイテムにエンティティIDを使用します。そうすることで、将来のmutationsは、特定のアイテムが含まれる場合にのみデータを無効にするか(例:{ type: 'Post', id: 5 })、'LIST'の場合にデータを無効にするか(例:{ type: 'Post', id: 'LIST' })を宣言できます。

'LIST'タグとID
  1. LISTは任意の文字列です。厳密に言えば、ALL*など、ここで好きなものを使用できます。カスタムIDを選択する際に重要なことは、クエリ結果によって返されるIDと衝突する可能性がないことを確認することです。クエリ結果に不明なIDがあり、リスクを冒したくない場合は、以下の3番目の点を使用できます。
  2. さらに多くの制御のために、多数のタグタイプを追加できます。
    • [{ type: 'Posts', id: 'LIST' }, { type: 'Posts', id: 'SVELTE_POSTS' }, { type: 'Posts', id: 'REACT_POSTS' }]
  3. 'LIST'のようなidの使用の概念が奇妙に思える場合は、常に別のtagTypeを追加してそのルートを無効にできますが、示されているようにidアプローチを使用することをお勧めします。

以下のシナリオを比較して、'LIST' IDを使用して動作を最適化する方法を確認できます。

あるタイプのすべてを無効化する

API定義
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
App.tsx
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がトリガーされると、addPostがルートタグを無効化するため、各PostDetailコンポーネントはisFetching状態に戻り、これにより、'Posts'を提供するすべてのクエリが再実行されます。ほとんどの場合、これはやりたいことではないかもしれません。画面に100件の投稿があり、すべてがgetPostクエリにサブスクライブしていると想像してください。この場合、100件のリクエストを作成し、サーバーに大量の不要なトラフィックを送信することになります。これは、そもそも回避しようとしていることです!ユーザーは最後に良好なキャッシュ結果を確認し、ブラウザの不具合以外には何も気づかない可能性がありますが、それでもこれを回避する必要があります。

リストを選択的に無効化する

API定義
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
App.tsx
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が発行されると、addPost'LIST' IDのみを無効化するため、PostsListのみがisFetching状態になります。これにより、getPostsが再実行されます(特定のIDを提供するため)。したがって、ネットワークタブでは、GET /postsに対して1つの新しいリクエストが発行されるだけです。単数形のgetPostクエリは無効化されていないため、addPostの結果として再実行されません。

情報

addPostミューテーションが個々のPostDetailコンポーネントを含むすべての投稿を更新することを意図しており、同時に新しいGET /postsリクエストを1つだけ作成する場合は、selectFromResultを使用してデータの一部を選択することで実行できます。

エラーをキャッシュに提供する

キャッシュに提供される情報は、正常なデータフェッチに限定されません。この概念を使用して、特定のエラーが発生した場合、その失敗したキャッシュデータに特定のtagprovideするようにRTK Queryに通知できます。別のエンドポイントは、そのtagのデータをinvalidateすることができ、コンポーネントがまだ失敗したデータにサブスクライブしている場合、RTK Queryに以前に失敗したエンドポイントを再試行するように指示します。

以下の例は、次の動作の例を示しています。

  • クエリが401 UNAUTHORIZEDのエラーコードで失敗した場合、UNAUTHORIZEDキャッシュタグを提供します。
  • クエリが異なるエラーで失敗した場合、UNKNOWN_ERRORキャッシュタグを提供します。
  • 成功した場合、UNAUTHORIZEDタグを使用してデータをinvalidateする「ログイン」ミューテーションを有効にします。
    これは、postByIdエンドポイントが次の場合に再発行されることをトリガーします。
    1. postByIdの最後の呼び出しで、未承認のエラーが発生した場合、および
    2. コンポーネントがまだキャッシュデータにサブスクライブしている場合
  • 呼び出されたときに、UNKNOWN_ERRORタグを使用してデータをinvalidateする「refetchErroredQueries」ミューテーションを有効にします。
    これは、postByIdエンドポイントが次の場合に再発行されることをトリガーします。
    1. postByIdの最後の呼び出しで、不明なエラーが発生した場合、および
    2. コンポーネントがまだキャッシュデータにサブスクライブしている場合
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'],
}),
}),
})

一般的なprovides/invalidatesの使用を抽象化する

特定のAPIスライスのタグをprovideおよびinvalidateするために記述されたコードは、次のようないくつかの要因に依存します。

  • バックエンドによって返されるデータの形状
  • 特定のクエリエンドポイントが提供すると予想されるタグ
  • 特定のミューテーションエンドポイントが無効化すると予想されるタグ
  • 無効化機能を使用したい範囲

APIスライスを宣言するとき、コードが重複していると感じるかもしれません。たとえば、特定のエントリのリストを提供する2つの別々のエンドポイントの場合、providesTags宣言は提供されるtagTypeのみが異なる場合があります。

例:

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' }],
}),
}),
})

特定のAPI用に設計されたヘルパー関数を定義して、エンドポイント定義全体でこのボイラープレートを削減すると便利かもしれません。例:

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'),
}),
}),
})

一般的なrestデータ形式用に設計された、タグの提供/無効化のためのさまざまな抽象化の例を、次のgistで見ることができます。これには、typescriptのサポート、および'LIST'スタイルの高度なタグ無効化'エラー'スタイルのタグ無効化の両方が含まれています:RTK Queryキャッシュユーティリティ