diff --git a/docs/src/pages/reference/QueryClient.md b/docs/src/pages/reference/QueryClient.md index 4858476ea4..4e34813aee 100644 --- a/docs/src/pages/reference/QueryClient.md +++ b/docs/src/pages/reference/QueryClient.md @@ -32,6 +32,7 @@ Its available methods are: - [`refetchQueries`](#queryclientrefetchqueries) - [`cancelQueries`](#queryclientcancelqueries) - [`removeQueries`](#queryclientremovequeries) +- [`resetQueries`](#queryclientresetqueries) - [`isFetching`](#queryclientisfetching) - [`getDefaultOptions`](#queryclientsetdefaultoptions) - [`setDefaultOptions`](#queryclientgetdefaultoptions) @@ -274,6 +275,30 @@ queryClient.removeQueries(queryKey, { exact: true }) This method does not return anything +## `queryClient.resetQueries` + +The `resetQueries` method can be used to reset queries in the cache to their +initial state based on their query keys or any other functionally accessible +property/state of the query. + +This will notify subscribers — unlike `clear`, which removes all +subscribers — and reset the query to its pre-loaded state — unlike +`invalidateQueries`. If a query has `initialData`, the query's data will be +reset to that. + +```js +queryClient.resetQueries(queryKey, { exact: true }) +``` + +**Options** + +- `queryKey?: QueryKey`: [Query Keys](../guides/query-keys) +- `filters?: QueryFilters`: [Query Filters](../guides/query-filters) + +**Returns** + +This method does not return anything + ## `queryClient.isFetching` This `isFetching` method returns an `integer` representing how many queries, if any, in the cache are currently fetching (including background-fetching, loading new pages, or loading more infinite query results) diff --git a/src/core/query.ts b/src/core/query.ts index ff37c78cb7..fec4bf4f64 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -124,6 +124,7 @@ export class Query { queryKey: QueryKey queryHash: string options!: QueryOptions + initialState: QueryState state: QueryState cacheTime!: number @@ -141,7 +142,8 @@ export class Query { this.cache = config.cache this.queryKey = config.queryKey this.queryHash = config.queryHash - this.state = config.state || this.getDefaultState(this.options) + this.initialState = config.state || this.getDefaultState(this.options) + this.state = this.initialState this.scheduleGc() } @@ -220,6 +222,11 @@ export class Query { this.cancel() } + reset(): void { + this.destroy() + this.setState(this.initialState) + } + isActive(): boolean { return this.observers.some(observer => observer.options.enabled !== false) } diff --git a/src/core/queryClient.ts b/src/core/queryClient.ts index 8161343fb6..af2a300744 100644 --- a/src/core/queryClient.ts +++ b/src/core/queryClient.ts @@ -132,6 +132,18 @@ export class QueryClient { }) } + resetQueries(filters?: QueryFilters): void + resetQueries(queryKey?: QueryKey, filters?: QueryFilters): void + resetQueries(arg1?: QueryKey | QueryFilters, arg2?: QueryFilters): void { + const [filters] = parseFilterArgs(arg1, arg2) + const queryCache = this.queryCache + notifyManager.batch(() => { + queryCache.findAll(filters).forEach(query => { + query.reset() + }) + }) + } + cancelQueries(filters?: QueryFilters, options?: CancelOptions): Promise cancelQueries( queryKey?: QueryKey, diff --git a/src/core/tests/queryCache.test.tsx b/src/core/tests/queryCache.test.tsx index 877e36c47b..1470d7d7e4 100644 --- a/src/core/tests/queryCache.test.tsx +++ b/src/core/tests/queryCache.test.tsx @@ -948,6 +948,56 @@ describe('queryCache', () => { expect(queryFn2).toHaveBeenCalledTimes(1) }) + test('should notify listeners when a query is reset', async () => { + const key = queryKey() + + const callback = jest.fn() + + await queryClient.prefetchQuery(key, () => 'data') + + queryCache.subscribe(callback) + + queryClient.resetQueries(key) + + expect(callback).toHaveBeenCalled() + }) + + test('resetQueries should reset query', async () => { + const key = queryKey() + + await queryClient.prefetchQuery(key, () => 'data') + + let state = queryClient.getQueryState(key) + expect(state?.data).toEqual('data') + expect(state?.status).toEqual('success') + + queryClient.resetQueries(key) + + state = queryClient.getQueryState(key) + + expect(state).toBeTruthy() + expect(state?.data).toBeUndefined() + expect(state?.status).toEqual('idle') + }) + + test('resetQueries should reset query data to initial data if set', async () => { + const key = queryKey() + + await queryClient.prefetchQuery(key, () => 'data', { + initialData: 'initial', + }) + + let state = queryClient.getQueryState(key) + expect(state?.data).toEqual('data') + + queryClient.resetQueries(key) + + state = queryClient.getQueryState(key) + + expect(state).toBeTruthy() + expect(state?.data).toEqual('initial') + }) + test('find should filter correctly', async () => { const key = queryKey() const testCache = new QueryCache()