diff --git a/docs/rtk-query/api/created-api/api-slice-utils.mdx b/docs/rtk-query/api/created-api/api-slice-utils.mdx index 7698b0c7f9..fb65a6bc2e 100644 --- a/docs/rtk-query/api/created-api/api-slice-utils.mdx +++ b/docs/rtk-query/api/created-api/api-slice-utils.mdx @@ -268,14 +268,12 @@ It returns an array that contains #### Example ```ts no-transpile -dispatch(api.util.selectInvalidatedBy(state, ['Post'])) -dispatch(api.util.selectInvalidatedBy(state, [{ type: 'Post', id: 1 }])) -dispatch( - api.util.selectInvalidatedBy(state, [ - { type: 'Post', id: 1 }, - { type: 'Post', id: 4 }, - ]) -) +const entries = api.util.selectInvalidatedBy(state, ['Post']) +const entries = api.util.selectInvalidatedBy(state, [{ type: 'Post', id: 1 }]) +const entries = api.util.selectInvalidatedBy(state, [ + { type: 'Post', id: 1 }, + { type: 'Post', id: 4 }, +]) ``` ### `invalidateTags` @@ -318,6 +316,39 @@ dispatch( ) ``` +### `selectCachedArgsForQuery` + +#### Signature + +```ts no-transpile +function selectCachedArgsForQuery( + state: RootState, + queryName: QueryName +): Array +``` + +- **Parameters** + - `state`: the root state + - `queryName`: a string matching an existing query endpoint name + +#### Description + +A function that can select arguments for currently cached queries. + +The function accepts two arguments + +- the root state and + +- the name of the query + +It returns an array that contains arguments used for each entry. + +#### Example + +```ts no-transpile +const args = api.util.selectCachedArgsForQuery(state, 'getPosts') +``` + ### `resetApiState` #### Signature diff --git a/docs/rtk-query/api/created-api/overview.mdx b/docs/rtk-query/api/created-api/overview.mdx index c64dd64b72..b515a2f5c8 100644 --- a/docs/rtk-query/api/created-api/overview.mdx +++ b/docs/rtk-query/api/created-api/overview.mdx @@ -61,6 +61,10 @@ type Api = { originalArgs: any queryCacheKey: string }> + selectCachedArgsForQuery: ( + state: FullState, + endpointName: EndpointName + ) => Array resetApiState: ActionCreator getRunningQueryThunk( endpointName: EndpointName, diff --git a/packages/toolkit/src/query/core/buildSelectors.ts b/packages/toolkit/src/query/core/buildSelectors.ts index d3a7361c70..58967bb6b1 100644 --- a/packages/toolkit/src/query/core/buildSelectors.ts +++ b/packages/toolkit/src/query/core/buildSelectors.ts @@ -5,6 +5,8 @@ import type { RootState as _RootState, RequestStatusFlags, QueryCacheKey, + QueryKeys, + QueryState, } from './apiState' import { QueryStatus, getRequestStatusFlags } from './apiState' import type { @@ -130,7 +132,12 @@ export function buildSelectors< const selectSkippedQuery = (state: RootState) => defaultQuerySubState const selectSkippedMutation = (state: RootState) => defaultMutationSubState - return { buildQuerySelector, buildMutationSelector, selectInvalidatedBy } + return { + buildQuerySelector, + buildMutationSelector, + selectInvalidatedBy, + selectCachedArgsForQuery, + } function withRequestFlags( substate: T @@ -238,4 +245,22 @@ export function buildSelectors< }) ) } + + function selectCachedArgsForQuery>( + state: RootState, + queryName: QueryName + ): Array> { + return Object.values(state[reducerPath].queries as QueryState) + .filter( + ( + entry + ): entry is Exclude< + QuerySubState, + { status: QueryStatus.uninitialized } + > => + entry?.endpointName === queryName && + entry.status !== QueryStatus.uninitialized + ) + .map((entry) => entry.originalArgs) + } } diff --git a/packages/toolkit/src/query/core/module.ts b/packages/toolkit/src/query/core/module.ts index 0b7d85b582..71be69936d 100644 --- a/packages/toolkit/src/query/core/module.ts +++ b/packages/toolkit/src/query/core/module.ts @@ -356,6 +356,16 @@ declare module '../apiTypes' { originalArgs: any queryCacheKey: string }> + + /** + * A function to select all arguments currently cached for a given endpoint. + * + * Can be used for mutations that want to do optimistic updates instead of invalidating a set of tags, but don't know exactly what they need to update. + */ + selectCachedArgsForQuery: >( + state: RootState, + queryName: QueryName + ) => Array> } /** * Endpoints based on the input endpoints provided to `createApi`, containing `select` and `action matchers`. @@ -527,13 +537,17 @@ export const coreModule = (): Module => ({ safeAssign(api, { reducer: reducer as any, middleware }) - const { buildQuerySelector, buildMutationSelector, selectInvalidatedBy } = - buildSelectors({ - serializeQueryArgs: serializeQueryArgs as any, - reducerPath, - }) + const { + buildQuerySelector, + buildMutationSelector, + selectInvalidatedBy, + selectCachedArgsForQuery, + } = buildSelectors({ + serializeQueryArgs: serializeQueryArgs as any, + reducerPath, + }) - safeAssign(api.util, { selectInvalidatedBy }) + safeAssign(api.util, { selectInvalidatedBy, selectCachedArgsForQuery }) const { buildInitiateQuery, diff --git a/packages/toolkit/src/query/tests/buildSelector.test.ts b/packages/toolkit/src/query/tests/buildSelector.test.ts index 5248dfcd29..5a62a1320d 100644 --- a/packages/toolkit/src/query/tests/buildSelector.test.ts +++ b/packages/toolkit/src/query/tests/buildSelector.test.ts @@ -52,4 +52,37 @@ describe('buildSelector', () => { const upperTitle = todoTitle.toUpperCase() expectExactType(upperTitle) }) + test.skip('selectCachedArgsForQuery typetest', () => { + interface Todo { + userId: number + id: number + title: string + completed: boolean + } + + type Todos = Array + + const exampleApi = createApi({ + reducerPath: 'api', + baseQuery: fetchBaseQuery({ + baseUrl: 'https://jsonplaceholder.typicode.com', + }), + endpoints: (build) => ({ + getTodos: build.query({ + query: () => '/todos', + }), + }), + }) + + const store = configureStore({ + reducer: { + [exampleApi.reducerPath]: exampleApi.reducer, + other: () => 1, + }, + }) + + expectExactType( + exampleApi.util.selectCachedArgsForQuery(store.getState(), 'getTodos') + ) + }) })