createApi
createApi
はRTK Queryの中核機能です。バックエンドAPIやその他の非同期ソースからデータを取得する方法を記述する一連の「エンドポイント」を定義できます。これには、データの取得と変換方法の設定も含まれます。これにより、データの取得とキャッシュ処理をカプセル化するReduxロジック(およびオプションでReactフック)を含む「APIスライス」構造が生成されます。APIスライス構造
通常、アプリケーションが通信する必要がある基本URLごとに、APIスライスは1つだけにする必要があります。例えば、サイトが/api/posts
と/api/users
の両方からデータを取得する場合、基本URLとして/api/
を持つ単一のAPIスライスと、posts
とusers
の個別のエンドポイント定義を使用します。これにより、エンドポイント間で自動再取得を定義することで、効果的に活用できます。タグの関係
保守性の観点から、エンドポイント定義を複数のファイルに分割し、それでもこれらのエンドポイントをすべて含む単一のAPIスライスを維持したい場合があります。コード分割を参照して、injectEndpoints
プロパティを使用して他のファイルからAPIエンドポイントを単一のAPIスライス定義に挿入する方法を確認してください。
- 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は、一般的なユースケースのためにfetch
を軽量にラップしたfetchBaseQueryというユーティリティをエクスポートしています。クエリのカスタマイズを参照して、fetchBaseQuery
が要件を満たさない場合を確認してください。
baseQuery関数の引数
args
- 指定されたエンドポイントのquery
関数の戻り値api
-BaseQueryApi
オブジェクトは以下を含みます。signal
- DOMリクエストの中断に使用できる、またはリクエストの中断済みかどうかを読み取ることができるAbortSignal
オブジェクト。abort
-signal
にアタッチされたAbortControllerのabort()
メソッド。dispatch
- 対応するReduxストアのstore.dispatch
メソッド。getState
- 現在のストアの状態にアクセスするために呼び出すことができる関数。extra
- configureStoreのgetDefaultMiddlewareオプションにthunk.extraArgumentとして提供されます。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
エンドポイントは、サーバーに対して実行する操作のセットです。ビルダー構文を使用してオブジェクトとして定義します。基本的なエンドポイントタイプには2種類あります。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
を参照します。
- 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
すべてのディスパッチされたアクションに渡される関数。これが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
またはbaseQuery
に渡されるobject
を返す関数にすることができます。 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
- DOMリクエストの中断に使用できる、またはリクエストの中断済みかどうかを読み取ることができるAbortSignal
オブジェクト。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
関数に3番目の引数として渡されます。
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
エンドポイントで使用されます。キャッシュされたデータのうち、再フェッチするか、キャッシュから削除する必要があるかを決定します。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
(オプション、クエリエンドポイントのみ)
このエンドポイントのみについて、api全体の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,
}),
}),
})
serializeQueryArgs
(オプション、クエリエンドポイントのみ)
クエリ引数に基づいてカスタムキャッシュキー値を返すために提供できます。
これは主に、シリアライズできない値がクエリ引数オブジェクトの一部として渡され、キャッシュキーから除外する必要がある場合に使用されます。エンドポイントが無限スクロール/ページネーションの実装など、単一のキャッシュエントリのみを持つ必要がある場合にも使用できます。
stringのみを返すことができる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
- ストアのためのディスパッチメソッド。getState
- ストアの現在の状態を取得するためのメソッド。extra
-configureStore
のgetDefaultMiddleware
オプションにthunk.extraArgument
として提供されたextra
。requestId
- クエリ/ミューテーションに対して生成された一意のID。queryFulfilled
-data
プロパティ(変換されたクエリ結果)とmeta
プロパティ(baseQuery
によって返されたmeta
)で解決される Promise。クエリが失敗した場合、この 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
- ストアのためのディスパッチメソッド。getState
- ストアの現在の状態を取得するためのメソッド。extra
-configureStore
のgetDefaultMiddleware
オプションにthunk.extraArgument
として提供されたextra
。requestId
- キャッシュエントリに対して生成された一意のID。cacheEntryRemoved
- アプリケーションで長すぎる間使用/購読されていないか、api.util.resetApiState
をディスパッチすることで、キャッシュエントリがキャッシュから削除された時点を待機できる Promise。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>
戻り値
「作成されたAPI」APIリファレンスを参照してください