diff --git a/docs/src/pages/docs/api.md b/docs/src/pages/docs/api.md index 5515e4e03d..5e2a41379c 100644 --- a/docs/src/pages/docs/api.md +++ b/docs/src/pages/docs/api.md @@ -361,6 +361,7 @@ Its available properties and methods are: - [`prefetchQuery`](#querycacheprefetchquery) - [`getQueryData`](#querycachegetquerydata) - [`setQueryData`](#querycachesetquerydata) +- [`refetchQueries`](#querycacherefetchqueries) - [`invalidateQueries`](#querycacheinvalidatequeries) - [`cancelQueries`](#querycachecancelqueries) - [`removeQueries`](#querycacheremovequeries) @@ -490,6 +491,52 @@ For convenience in syntax, you can also pass an updater function which receives setQueryData(queryKey, oldData => newData) ``` +## `queryCache.refetchQueries` + +The `refetchQueries` method can be used to refetch queries based on certain conditions. + +Examples: + +```js +// refetch all queries: +await queryCache.refetchQueries() + +// refetch all stale queries: +await queryCache.refetchQueries([], { stale: true }) + +// refetch all stale and active queries: +await queryCache.refetchQueries([], { stale: true, active: true }) + +// refetch all queries partially matching a query key: +await queryCache.refetchQueries(['posts']) + +// refetch all queries exactly matching a query key: +await queryCache.refetchQueries(['posts', 1], { exact: true }) +``` + +**Options** + +- `queryKeyOrPredicateFn` can either be a [Query Key](#query-keys) or a `Function` + - `queryKey: QueryKey` + - If a query key is passed, queries will be filtered to those where this query key is included in the existing query's query key. This means that if you passed a query key of `'todos'`, it would match queries with the `todos`, `['todos']`, and `['todos', 5]`. See [Query Keys](./guides/queries#query-keys) for more information. + - `query => boolean` + - This predicate function will be called for every single query in the cache and be expected to return truthy for queries that are `found`. + - The `exact` option has no effect when using a function +- `exact?: boolean` + - If you don't want to search queries inclusively by query key, you can pass the `exact: true` option to return only the query with the exact query key you have passed. Remember to destructure it out of the array! +- `active?: boolean` + - When set to `true` it will refetch active queries. + - When set to `false` it will refetch inactive queries. +- `stale?: boolean` + - When set to `true` it will match on stale queries. + - When set to `false` it will match on fresh queries. +- `throwOnError?: boolean` + - When set to `true`, this method will throw if any of the query refetch tasks fail. + +**Returns** + +This function returns a promise that will resolve when all of the queries are done being refetched. By default, it **will not** throw an error if any of those queries refetches fail, but this can be configured by setting the `throwOnError` option to `true` + ## `queryCache.invalidateQueries` The `invalidateQueries` method can be used to invalidate and refetch single or multiple queries in the cache based on their query keys or any other functionally accessible property/state of the query. By default, all matching queries are immediately marked as stale and active queries are refetched in the background. diff --git a/src/core/tests/queryCache.test.tsx b/src/core/tests/queryCache.test.tsx index 4fec201012..ab24aaa797 100644 --- a/src/core/tests/queryCache.test.tsx +++ b/src/core/tests/queryCache.test.tsx @@ -7,6 +7,7 @@ import { } from '../../react/tests/utils' import { QueryCache, queryCache as defaultQueryCache } from '../..' import { isCancelledError, isError } from '../utils' +import { QueryObserver } from '../queryObserver' describe('queryCache', () => { test('setQueryData does not crash if query could not be found', () => { @@ -230,6 +231,89 @@ describe('queryCache', () => { ) }) + test('refetchQueries should refetch all queries when no arguments are given', async () => { + const key1 = queryKey() + const key2 = queryKey() + const queryFn1 = jest.fn() + const queryFn2 = jest.fn() + const cache = new QueryCache() + await cache.fetchQuery(key1, queryFn1) + await cache.fetchQuery(key2, queryFn2) + await cache.refetchQueries() + cache.clear() + expect(queryFn1).toHaveBeenCalledTimes(2) + expect(queryFn2).toHaveBeenCalledTimes(2) + }) + + test('refetchQueries should be able to refetch all fresh queries', async () => { + const key1 = queryKey() + const key2 = queryKey() + const queryFn1 = jest.fn() + const queryFn2 = jest.fn() + const cache = new QueryCache() + await cache.fetchQuery(key1, queryFn1) + await cache.fetchQuery(key2, queryFn2) + await cache.refetchQueries([], { stale: false }) + cache.clear() + expect(queryFn1).toHaveBeenCalledTimes(2) + expect(queryFn2).toHaveBeenCalledTimes(2) + }) + + test('refetchQueries should be able to refetch all stale queries', async () => { + const key1 = queryKey() + const key2 = queryKey() + const queryFn1 = jest.fn() + const queryFn2 = jest.fn() + const cache = new QueryCache() + await cache.fetchQuery(key1, queryFn1) + await cache.fetchQuery(key2, queryFn2) + cache.getQuery(key1)!.invalidate() + await cache.refetchQueries([], { stale: true }) + cache.clear() + expect(queryFn1).toHaveBeenCalledTimes(2) + expect(queryFn2).toHaveBeenCalledTimes(1) + }) + + test('refetchQueries should be able to refetch all stale and active queries', async () => { + const key1 = queryKey() + const key2 = queryKey() + const queryFn1 = jest.fn() + const queryFn2 = jest.fn() + const cache = new QueryCache() + await cache.fetchQuery(key1, queryFn1) + await cache.fetchQuery(key2, queryFn2) + const query1 = cache.getQuery(key1)! + query1.invalidate() + const observer = query1.subscribe() + await cache.refetchQueries([], { active: true, stale: true }) + observer.unsubscribe() + cache.clear() + expect(queryFn1).toHaveBeenCalledTimes(2) + expect(queryFn2).toHaveBeenCalledTimes(1) + }) + + test('refetchQueries should be able to refetch all inactive queries', async () => { + const key1 = queryKey() + const key2 = queryKey() + const queryFn1 = jest.fn() + const queryFn2 = jest.fn() + const cache = new QueryCache() + await cache.fetchQuery(key1, queryFn1) + await cache.fetchQuery(key2, queryFn2) + const query1 = cache.getQuery(key1)! + const observer = new QueryObserver({ + ...query1.config, + staleTime: Infinity, + }) + const unsubscribe = observer.subscribe() + await cache.refetchQueries([], { active: false }) + expect(queryFn1).toHaveBeenCalledTimes(1) + unsubscribe() + cache.clear() + expect(queryFn1).toHaveBeenCalledTimes(1) + expect(queryFn2).toHaveBeenCalledTimes(2) + }) + test('getQueries should return queries that partially match queryKey', async () => { const key1 = queryKey() const key2 = queryKey()