diff --git a/docs/react/community/tkdodos-blog.md b/docs/react/community/tkdodos-blog.md index 174a969f1a..d567bdaa8e 100644 --- a/docs/react/community/tkdodos-blog.md +++ b/docs/react/community/tkdodos-blog.md @@ -8,7 +8,7 @@ React Query maintainer [TkDodo](https://twitter.com/tkdodo) has a series of blog ## [#1: Practical React Query](https://tkdodo.eu/blog/practical-react-query) -> An advanced introduction to React Query, showing practical tips that go beyond the docs. It covers explaining the defaults (`staleTime` vs. `cacheTime`), concepts like keeping server and client state separate, handling dependencies and creating custom hooks, as well as outlining why the `enabled` option is very powerful. [Read more...](https://tkdodo.eu/blog/practical-react-query) +> An advanced introduction to React Query, showing practical tips that go beyond the docs. It covers explaining the defaults (`staleTime` vs. `gcTime`), concepts like keeping server and client state separate, handling dependencies and creating custom hooks, as well as outlining why the `enabled` option is very powerful. [Read more...](https://tkdodo.eu/blog/practical-react-query) ## [#2: React Query Data Transformations](https://tkdodo.eu/blog/react-query-data-transformations) diff --git a/docs/react/guides/caching.md b/docs/react/guides/caching.md index bbaf0ee2aa..675168354f 100644 --- a/docs/react/guides/caching.md +++ b/docs/react/guides/caching.md @@ -14,7 +14,7 @@ This caching example illustrates the story and lifecycle of: - Inactive Queries - Garbage Collection -Let's assume we are using the default `cacheTime` of **5 minutes** and the default `staleTime` of `0`. +Let's assume we are using the default `gcTime` of **5 minutes** and the default `staleTime` of `0`. - A new instance of `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` mounts. - Since no other queries have been made with the `['todos']` query key, this query will show a hard loading state and make a network request to fetch the data. @@ -26,7 +26,7 @@ Let's assume we are using the default `cacheTime` of **5 minutes** and the defau - Note that regardless of whether both `fetchTodos` query functions are identical or not, both queries' [`status`](../reference/useQuery) are updated (including `isFetching`, `isLoading`, and other related values) because they have the same query key. - When the request completes successfully, the cache's data under the `['todos']` key is updated with the new data, and both instances are updated with the new data. - Both instances of the `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` query are unmounted and no longer in use. - - Since there are no more active instances of this query, a cache timeout is set using `cacheTime` to delete and garbage collect the query (defaults to **5 minutes**). + - Since there are no more active instances of this query, a garbage collection timeout is set using `gcTime` to delete and garbage collect the query (defaults to **5 minutes**). - Before the cache timeout has completed, another instance of `useQuery({ queryKey: ['todos'], queyFn: fetchTodos })` mounts. The query immediately returns the available cached data while the `fetchTodos` function is being run in the background. When it completes successfully, it will populate the cache with fresh data. - The final instance of `useQuery({ queryKey: ['todos'], queryFn: fetchTodos })` unmounts. - No more instances of `useQuery({ queyKey: ['todos'], queryFn: fetchTodos })` appear within **5 minutes**. diff --git a/docs/react/guides/important-defaults.md b/docs/react/guides/important-defaults.md index 57d867c60e..d2f78d68e0 100644 --- a/docs/react/guides/important-defaults.md +++ b/docs/react/guides/important-defaults.md @@ -20,7 +20,7 @@ Out of the box, TanStack Query is configured with **aggressive but sane** defaul - Query results that have no more active instances of `useQuery`, `useInfiniteQuery` or query observers are labeled as "inactive" and remain in the cache in case they are used again at a later time. - By default, "inactive" queries are garbage collected after **5 minutes**. - > To change this, you can alter the default `cacheTime` for queries to something other than `1000 * 60 * 5` milliseconds. + > To change this, you can alter the default `gcTime` for queries to something other than `1000 * 60 * 5` milliseconds. - Queries that fail are **silently retried 3 times, with exponential backoff delay** before capturing and displaying an error to the UI. diff --git a/docs/react/guides/migrating-to-v5.md b/docs/react/guides/migrating-to-v5.md index 14f71e16ee..f50c54419a 100644 --- a/docs/react/guides/migrating-to-v5.md +++ b/docs/react/guides/migrating-to-v5.md @@ -246,6 +246,24 @@ The `Hydrate` component has been renamed to `HydrationBoundary`. The `Hydrate` c - + ``` -``` +### Rename `cacheTime` to `gcTime` + +Almost everyone gets `cacheTime` wrong. It sounds like "the amount of time that data is cached for", but that is not correct. + +`cacheTime` does nothing as long as a query is still in used. It only kicks in as soon as the query becomes unused. After the time has passed, data will be "garbage collected" to avoid the cache from growing. + +`gc` is referring to "garbage collect" time. It's a bit more technical, but also a quite [well known abbreviation](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)) in computer science. +```diff +const MINUTE = 1000 * 60; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { +- cacheTime: 10 * MINUTE, ++ gcTime: 10 * MINUTE, + }, + }, +}) +``` \ No newline at end of file diff --git a/docs/react/guides/mutations.md b/docs/react/guides/mutations.md index 790396deb5..33e63b2249 100644 --- a/docs/react/guides/mutations.md +++ b/docs/react/guides/mutations.md @@ -355,7 +355,7 @@ const persister = createSyncStoragePersister({ const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) diff --git a/docs/react/guides/prefetching.md b/docs/react/guides/prefetching.md index 1217a9f068..01a0865114 100644 --- a/docs/react/guides/prefetching.md +++ b/docs/react/guides/prefetching.md @@ -21,7 +21,7 @@ const prefetchTodos = async () => { - If data for this query is already in the cache and **not invalidated**, the data will not be fetched - If a `staleTime` is passed eg. `prefetchQuery({queryKey: ['todos'], queryFn: fn, staleTime: 5000 })` and the data is older than the specified staleTime, the query will be fetched -- If no instances of `useQuery` appear for a prefetched query, it will be deleted and garbage collected after the time specified in `cacheTime`. +- If no instances of `useQuery` appear for a prefetched query, it will be deleted and garbage collected after the time specified in `gcTime`. ## Manually Priming a Query diff --git a/docs/react/guides/ssr.md b/docs/react/guides/ssr.md index 8cfa747a61..b1fa8a8e37 100644 --- a/docs/react/guides/ssr.md +++ b/docs/react/guides/ssr.md @@ -360,10 +360,10 @@ This refetching of stale queries is a perfect match when caching markup in a CDN ### High memory consumption on server -In case you are creating the `QueryClient` for every request, React Query creates the isolated cache for this client, which is preserved in memory for the `cacheTime` period. That may lead to high memory consumption on server in case of high number of requests during that period. +In case you are creating the `QueryClient` for every request, React Query creates the isolated cache for this client, which is preserved in memory for the `gcTime` period. That may lead to high memory consumption on server in case of high number of requests during that period. -On the server, `cacheTime` defaults to `Infinity` which disables manual garbage collection and will automatically clear memory once a request has finished. If you are explicitly setting a non-Infinity `cacheTime` then you will be responsible for clearing the cache early. +On the server, `gcTime` defaults to `Infinity` which disables manual garbage collection and will automatically clear memory once a request has finished. If you are explicitly setting a non-Infinity `gcTime` then you will be responsible for clearing the cache early. To clear the cache after it is not needed and to lower memory consumption, you can add a call to [`queryClient.clear()`](../reference/QueryClient#queryclientclear) after the request is handled and dehydrated state has been sent to the client. -Alternatively, you can set a smaller `cacheTime`. +Alternatively, you can set a smaller `gcTime`. diff --git a/docs/react/plugins/createAsyncStoragePersister.md b/docs/react/plugins/createAsyncStoragePersister.md index d2107bce66..5812c32f3e 100644 --- a/docs/react/plugins/createAsyncStoragePersister.md +++ b/docs/react/plugins/createAsyncStoragePersister.md @@ -33,7 +33,7 @@ import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persi const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) diff --git a/docs/react/plugins/createSyncStoragePersister.md b/docs/react/plugins/createSyncStoragePersister.md index 46c28f1bb5..ea5243d701 100644 --- a/docs/react/plugins/createSyncStoragePersister.md +++ b/docs/react/plugins/createSyncStoragePersister.md @@ -31,7 +31,7 @@ import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persist const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) diff --git a/docs/react/plugins/persistQueryClient.md b/docs/react/plugins/persistQueryClient.md index dc2b2f00eb..e673c33813 100644 --- a/docs/react/plugins/persistQueryClient.md +++ b/docs/react/plugins/persistQueryClient.md @@ -13,11 +13,11 @@ This is set of utilities for interacting with "persisters" which save your query ## How It Works -**IMPORTANT** - for persist to work properly, you probably want to pass `QueryClient` a `cacheTime` value to override the default during hydration (as shown above). +**IMPORTANT** - for persist to work properly, you probably want to pass `QueryClient` a `gcTime` value to override the default during hydration (as shown above). If it is not set when creating the `QueryClient` instance, it will default to `300000` (5 minutes) for hydration, and the stored cache will be discarded after 5 minutes of inactivity. This is the default garbage collection behavior. -It should be set as the same value or higher than persistQueryClient's `maxAge` option. E.g. if `maxAge` is 24 hours (the default) then `cacheTime` should be 24 hours or higher. If lower than `maxAge`, garbage collection will kick in and discard the stored cache earlier than expected. +It should be set as the same value or higher than persistQueryClient's `maxAge` option. E.g. if `maxAge` is 24 hours (the default) then `gcTime` should be 24 hours or higher. If lower than `maxAge`, garbage collection will kick in and discard the stored cache earlier than expected. You can also pass it `Infinity` to disable garbage collection behavior entirely. @@ -25,7 +25,7 @@ You can also pass it `Infinity` to disable garbage collection behavior entirely. const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) @@ -187,7 +187,7 @@ import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persist const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) diff --git a/docs/react/reference/QueryClient.md b/docs/react/reference/QueryClient.md index 0453e71056..41a1bd4cf9 100644 --- a/docs/react/reference/QueryClient.md +++ b/docs/react/reference/QueryClient.md @@ -221,7 +221,7 @@ This distinction is more a "convenience" for ts devs that know which structure w ## `queryClient.setQueryData` -`setQueryData` is a synchronous function that can be used to immediately update a query's cached data. If the query does not exist, it will be created. **If the query is not utilized by a query hook in the default `cacheTime` of 5 minutes, the query will be garbage collected**. To update multiple queries at once and match query keys partially, you need to use [`queryClient.setQueriesData`](#queryclientsetqueriesdata) instead. +`setQueryData` is a synchronous function that can be used to immediately update a query's cached data. If the query does not exist, it will be created. **If the query is not utilized by a query hook in the default `gcTime` of 5 minutes, the query will be garbage collected**. To update multiple queries at once and match query keys partially, you need to use [`queryClient.setQueriesData`](#queryclientsetqueriesdata) instead. > The difference between using `setQueryData` and `fetchQuery` is that `setQueryData` is sync and assumes that you already synchronously have the data available. If you need to fetch the data asynchronously, it's suggested that you either refetch the query key or use `fetchQuery` to handle the asynchronous fetch. diff --git a/docs/react/reference/useMutation.md b/docs/react/reference/useMutation.md index eba51cdafb..0e32e637b3 100644 --- a/docs/react/reference/useMutation.md +++ b/docs/react/reference/useMutation.md @@ -20,7 +20,7 @@ const { status, } = useMutation({ mutationFn, - cacheTime, + gcTime, mutationKey, networkMode, onError, @@ -46,7 +46,7 @@ mutate(variables, { - **Required** - A function that performs an asynchronous task and returns a promise. - `variables` is an object that `mutate` will pass to your `mutationFn` -- `cacheTime: number | Infinity` +- `gcTime: number | Infinity` - The time in milliseconds that unused/inactive cache data remains in memory. When a mutation's cache becomes unused or inactive, that cache data will be garbage collected after this duration. When different cache times are specified, the longest one will be used. - If set to `Infinity`, will disable garbage collection - `mutationKey: string` diff --git a/docs/react/reference/useQuery.md b/docs/react/reference/useQuery.md index 175d102705..3039e8356f 100644 --- a/docs/react/reference/useQuery.md +++ b/docs/react/reference/useQuery.md @@ -29,7 +29,7 @@ const { } = useQuery({ queryKey, queryFn, - cacheTime, + gcTime, enabled, networkMode, initialData, @@ -91,9 +91,9 @@ const { - Defaults to `0` - The time in milliseconds after data is considered stale. This value only applies to the hook it is defined on. - If set to `Infinity`, the data will never be considered stale -- `cacheTime: number | Infinity` +- `gcTime: number | Infinity` - Defaults to `5 * 60 * 1000` (5 minutes) or `Infinity` during SSR - - The time in milliseconds that unused/inactive cache data remains in memory. When a query's cache becomes unused or inactive, that cache data will be garbage collected after this duration. When different cache times are specified, the longest one will be used. + - The time in milliseconds that unused/inactive cache data remains in memory. When a query's cache becomes unused or inactive, that cache data will be garbage collected after this duration. When different garbage collection times are specified, the longest one will be used. - If set to `Infinity`, will disable garbage collection - `queryKeyHashFn: (queryKey: QueryKey) => string` - Optional diff --git a/docs/vue/guides/mutations.md b/docs/vue/guides/mutations.md index 7d0d5ebb34..f8e593b513 100644 --- a/docs/vue/guides/mutations.md +++ b/docs/vue/guides/mutations.md @@ -63,7 +63,7 @@ function addTodo() { const client = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) diff --git a/docs/vue/guides/ssr.md b/docs/vue/guides/ssr.md index d0995a5b07..7a0098cd0c 100644 --- a/docs/vue/guides/ssr.md +++ b/docs/vue/guides/ssr.md @@ -213,10 +213,10 @@ This refetching of stale queries is a perfect match when caching markup in a CDN ### High memory consumption on server -In case you are creating the `QueryClient` for every request, Vue Query creates the isolated cache for this client, which is preserved in memory for the `cacheTime` period. That may lead to high memory consumption on server in case of high number of requests during that period. +In case you are creating the `QueryClient` for every request, Vue Query creates the isolated cache for this client, which is preserved in memory for the `gcTime` period. That may lead to high memory consumption on server in case of high number of requests during that period. -On the server, `cacheTime` defaults to `Infinity` which disables manual garbage collection and will automatically clear memory once a request has finished. If you are explicitly setting a non-Infinity `cacheTime` then you will be responsible for clearing the cache early. +On the server, `gcTime` defaults to `Infinity` which disables manual garbage collection and will automatically clear memory once a request has finished. If you are explicitly setting a non-Infinity `gcTime` then you will be responsible for clearing the cache early. To clear the cache after it is not needed and to lower memory consumption, you can add a call to [`queryClient.clear()`](../reference/QueryClient#queryclientclear) after the request is handled and dehydrated state has been sent to the client. -Alternatively, you can set a smaller `cacheTime`. +Alternatively, you can set a smaller `gcTime`. diff --git a/examples/react/basic-typescript/src/index.tsx b/examples/react/basic-typescript/src/index.tsx index 903aa351b4..ca0f39faaf 100644 --- a/examples/react/basic-typescript/src/index.tsx +++ b/examples/react/basic-typescript/src/index.tsx @@ -10,7 +10,7 @@ import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }); diff --git a/examples/react/offline/src/App.jsx b/examples/react/offline/src/App.jsx index 094a9a0dcb..68a37eed6e 100644 --- a/examples/react/offline/src/App.jsx +++ b/examples/react/offline/src/App.jsx @@ -32,7 +32,7 @@ const location = new ReactLocation(); const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours staleTime: 2000, retry: 0, }, diff --git a/examples/react/playground/src/index.jsx b/examples/react/playground/src/index.jsx index a2d5123f38..46ed043a96 100644 --- a/examples/react/playground/src/index.jsx +++ b/examples/react/playground/src/index.jsx @@ -31,7 +31,7 @@ const queryClient = new QueryClient(); function Root() { const [staleTime, setStaleTime] = React.useState(1000); - const [cacheTime, setCacheTime] = React.useState(3000); + const [gcTime, setgcTime] = React.useState(3000); const [localErrorRate, setErrorRate] = React.useState(errorRate); const [localFetchTimeMin, setLocalFetchTimeMin] = React.useState(queryTimeMin); @@ -48,17 +48,16 @@ function Root() { queryClient.setDefaultOptions({ queries: { staleTime, - cacheTime, + gcTime, }, }); - }, [cacheTime, staleTime]); + }, [gcTime, staleTime]); return (

- The "staleTime" and "cacheTime" durations have been altered in this - example to show how query stale-ness and query caching work on a - granular level + The "staleTime" and "gcTime" durations have been altered in this example + to show how query stale-ness and query caching work on a granular level

Stale Time:{" "} @@ -72,13 +71,13 @@ function Root() { />
- Cache Time:{" "} + Garbage collection Time:{" "} setCacheTime(parseFloat(e.target.value, 10))} + value={gcTime} + onChange={(e) => setgcTime(parseFloat(e.target.value, 10))} style={{ width: "100px" }} />
diff --git a/examples/solid/basic-typescript/src/index.tsx b/examples/solid/basic-typescript/src/index.tsx index d4b79e1005..1334ac9a53 100644 --- a/examples/solid/basic-typescript/src/index.tsx +++ b/examples/solid/basic-typescript/src/index.tsx @@ -11,7 +11,7 @@ import { render } from 'solid-js/web' const queryClient = new QueryClient({ defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, // 24 hours + gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) diff --git a/examples/vue/persister/src/main.ts b/examples/vue/persister/src/main.ts index 61a93de4a0..11dcb67f7b 100644 --- a/examples/vue/persister/src/main.ts +++ b/examples/vue/persister/src/main.ts @@ -9,7 +9,7 @@ const vueQueryOptions: VueQueryPluginOptions = { queryClientConfig: { defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, + gcTime: 1000 * 60 * 60 * 24, staleTime: 1000 * 60 * 60 * 24, }, }, diff --git a/packages/query-core/src/mutation.ts b/packages/query-core/src/mutation.ts index 803787268d..5e693a3ca9 100644 --- a/packages/query-core/src/mutation.ts +++ b/packages/query-core/src/mutation.ts @@ -104,7 +104,7 @@ export class Mutation< this.#observers = [] this.state = config.state || getDefaultState() - this.updateCacheTime(this.options.cacheTime) + this.updateGcTime(this.options.gcTime) this.scheduleGc() } diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index e8ad9eb5ad..3a1144b5f0 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -183,7 +183,7 @@ export class Query< ): void { this.options = { ...this.#defaultOptions, ...options } - this.updateCacheTime(this.options.cacheTime) + this.updateGcTime(this.options.gcTime) } protected optionalRemove() { diff --git a/packages/query-core/src/removable.ts b/packages/query-core/src/removable.ts index 5b257fa6ea..bf353266ca 100644 --- a/packages/query-core/src/removable.ts +++ b/packages/query-core/src/removable.ts @@ -1,7 +1,7 @@ import { isServer, isValidTimeout } from './utils' export abstract class Removable { - cacheTime!: number + gcTime!: number #gcTimeout?: ReturnType destroy(): void { @@ -11,18 +11,18 @@ export abstract class Removable { protected scheduleGc(): void { this.clearGcTimeout() - if (isValidTimeout(this.cacheTime)) { + if (isValidTimeout(this.gcTime)) { this.#gcTimeout = setTimeout(() => { this.optionalRemove() - }, this.cacheTime) + }, this.gcTime) } } - protected updateCacheTime(newCacheTime: number | undefined): void { - // Default to 5 minutes (Infinity for server-side) if no cache time is set - this.cacheTime = Math.max( - this.cacheTime || 0, - newCacheTime ?? (isServer ? Infinity : 5 * 60 * 1000), + protected updateGcTime(newGcTime: number | undefined): void { + // Default to 5 minutes (Infinity for server-side) if no gcTime is set + this.gcTime = Math.max( + this.gcTime || 0, + newGcTime ?? (isServer ? Infinity : 5 * 60 * 1000), ) } diff --git a/packages/query-core/src/tests/hydration.test.tsx b/packages/query-core/src/tests/hydration.test.tsx index 5b6601914e..2bf773b949 100644 --- a/packages/query-core/src/tests/hydration.test.tsx +++ b/packages/query-core/src/tests/hydration.test.tsx @@ -119,13 +119,13 @@ describe('dehydration and rehydration', () => { queryClient.clear() }) - test('should use the cache time from the client', async () => { + test('should use the garbage collection time from the client', async () => { const queryCache = new QueryCache() const queryClient = createQueryClient({ queryCache }) await queryClient.prefetchQuery({ queryKey: ['string'], queryFn: () => fetchData('string'), - cacheTime: 50, + gcTime: 50, }) const dehydrated = dehydrate(queryClient) const stringified = JSON.stringify(dehydrated) diff --git a/packages/query-core/src/tests/mutationCache.test.tsx b/packages/query-core/src/tests/mutationCache.test.tsx index eeb7ec7c0d..6636da10eb 100644 --- a/packages/query-core/src/tests/mutationCache.test.tsx +++ b/packages/query-core/src/tests/mutationCache.test.tsx @@ -199,14 +199,14 @@ describe('mutationCache', () => { }) describe('garbage collection', () => { - test('should remove unused mutations after cacheTime has elapsed', async () => { + test('should remove unused mutations after gcTime has elapsed', async () => { const testCache = new MutationCache() const testClient = createQueryClient({ mutationCache: testCache }) const onSuccess = jest.fn() await executeMutation(testClient, { mutationKey: ['a', 1], variables: 1, - cacheTime: 10, + gcTime: 10, mutationFn: () => Promise.resolve(), onSuccess, }) @@ -223,7 +223,7 @@ describe('mutationCache', () => { const queryClient = createQueryClient() const observer = new MutationObserver(queryClient, { variables: 1, - cacheTime: 10, + gcTime: 10, mutationFn: () => Promise.resolve(), }) const unsubscribe = observer.subscribe(() => undefined) @@ -246,7 +246,7 @@ describe('mutationCache', () => { const onSuccess = jest.fn() const observer = new MutationObserver(queryClient, { variables: 1, - cacheTime: 10, + gcTime: 10, mutationFn: async () => { await sleep(20) return 'data' @@ -258,22 +258,22 @@ describe('mutationCache', () => { unsubscribe() expect(queryClient.getMutationCache().getAll()).toHaveLength(1) await sleep(10) - // unsubscribe should not remove even though cacheTime has elapsed b/c mutation is still loading + // unsubscribe should not remove even though gcTime has elapsed b/c mutation is still loading expect(queryClient.getMutationCache().getAll()).toHaveLength(1) await sleep(10) - // should be removed after an additional cacheTime wait + // should be removed after an additional gcTime wait await waitFor(() => { expect(queryClient.getMutationCache().getAll()).toHaveLength(0) }) expect(onSuccess).toHaveBeenCalledTimes(1) }) - test('should call callbacks even with cacheTime 0 and mutation still loading', async () => { + test('should call callbacks even with gcTime 0 and mutation still loading', async () => { const queryClient = createQueryClient() const onSuccess = jest.fn() const observer = new MutationObserver(queryClient, { variables: 1, - cacheTime: 0, + gcTime: 0, mutationFn: async () => { return 'data' }, diff --git a/packages/query-core/src/tests/query.test.tsx b/packages/query-core/src/tests/query.test.tsx index 0faf7139b3..145e79a8ed 100644 --- a/packages/query-core/src/tests/query.test.tsx +++ b/packages/query-core/src/tests/query.test.tsx @@ -28,25 +28,25 @@ describe('query', () => { queryClient.clear() }) - test('should use the longest cache time it has seen', async () => { + test('should use the longest garbage collection time it has seen', async () => { const key = queryKey() await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data', - cacheTime: 100, + gcTime: 100, }) await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data', - cacheTime: 200, + gcTime: 200, }) await queryClient.prefetchQuery({ queryKey: key, queryFn: () => 'data', - cacheTime: 10, + gcTime: 10, }) const query = queryCache.find({ queryKey: key })! - expect(query.cacheTime).toBe(200) + expect(query.gcTime).toBe(200) }) it('should continue retry after focus regain and resolve all promises', async () => { @@ -461,7 +461,7 @@ describe('query', () => { expect(query.state.status).toBe('error') }) - test('queries with cacheTime 0 should be removed immediately after unsubscribing', async () => { + test('queries with gcTime 0 should be removed immediately after unsubscribing', async () => { const key = queryKey() let count = 0 const observer = new QueryObserver(queryClient, { @@ -470,7 +470,7 @@ describe('query', () => { count++ return 'data' }, - cacheTime: 0, + gcTime: 0, staleTime: Infinity, }) const unsubscribe1 = observer.subscribe(() => undefined) @@ -492,7 +492,7 @@ describe('query', () => { const observer = new QueryObserver(queryClient, { queryKey: key, queryFn: async () => 'data', - cacheTime: 0, + gcTime: 0, }) expect(queryCache.find({ queryKey: key })).toBeDefined() const unsubscribe = observer.subscribe(() => undefined) @@ -511,7 +511,7 @@ describe('query', () => { await sleep(20) return 'data' }, - cacheTime: 10, + gcTime: 10, }) const unsubscribe = observer.subscribe(() => undefined) await sleep(20) @@ -519,7 +519,7 @@ describe('query', () => { observer.refetch() unsubscribe() await sleep(10) - // unsubscribe should not remove even though cacheTime has elapsed b/c query is still fetching + // unsubscribe should not remove even though gcTime has elapsed b/c query is still fetching expect(queryCache.find({ queryKey: key })).toBeDefined() await sleep(10) // should be removed after an additional staleTime wait @@ -533,7 +533,7 @@ describe('query', () => { const observer = new QueryObserver(queryClient, { queryKey: key, queryFn: async () => 'data', - cacheTime: 0, + gcTime: 0, }) expect(queryCache.find({ queryKey: key })).toBeDefined() const unsubscribe = observer.subscribe(() => undefined) diff --git a/packages/query-core/src/tests/queryClient.test.tsx b/packages/query-core/src/tests/queryClient.test.tsx index 0dca06295a..648cd9605b 100644 --- a/packages/query-core/src/tests/queryClient.test.tsx +++ b/packages/query-core/src/tests/queryClient.test.tsx @@ -43,14 +43,14 @@ describe('queryClient', () => { const testClient = createQueryClient({ defaultOptions: { - queries: { cacheTime: Infinity }, + queries: { gcTime: Infinity }, }, }) const fetchData = () => Promise.resolve('data') await testClient.prefetchQuery({ queryKey: key, queryFn: fetchData }) const newQuery = testClient.getQueryCache().find({ queryKey: key }) - expect(newQuery?.options.cacheTime).toBe(Infinity) + expect(newQuery?.options.gcTime).toBe(Infinity) }) test('should get defaultOptions', async () => { @@ -578,7 +578,7 @@ describe('queryClient', () => { expect(second).toBe(first) }) - test('should be able to fetch when cache time is set to 0 and then be removed', async () => { + test('should be able to fetch when garbage collection time is set to 0 and then be removed', async () => { const key1 = queryKey() const result = await queryClient.fetchQuery({ queryKey: key1, @@ -586,7 +586,7 @@ describe('queryClient', () => { await sleep(10) return 1 }, - cacheTime: 0, + gcTime: 0, }) expect(result).toEqual(1) await waitFor(() => @@ -594,7 +594,7 @@ describe('queryClient', () => { ) }) - test('should keep a query in cache if cache time is Infinity', async () => { + test('should keep a query in cache if garbage collection time is Infinity', async () => { const key1 = queryKey() const result = await queryClient.fetchQuery({ queryKey: key1, @@ -602,7 +602,7 @@ describe('queryClient', () => { await sleep(10) return 1 }, - cacheTime: Infinity, + gcTime: Infinity, }) const result2 = queryClient.getQueryData(key1) expect(result).toEqual(1) @@ -778,7 +778,7 @@ describe('queryClient', () => { expect(mockLogger.error).toHaveBeenCalled() }) - test('should be garbage collected after cacheTime if unused', async () => { + test('should be garbage collected after gcTime if unused', async () => { const key = queryKey() await queryClient.prefetchQuery({ @@ -786,7 +786,7 @@ describe('queryClient', () => { queryFn: async () => { return 'data' }, - cacheTime: 10, + gcTime: 10, }) expect(queryCache.find({ queryKey: key })).toBeDefined() await sleep(15) diff --git a/packages/query-core/src/tests/queryObserver.test.tsx b/packages/query-core/src/tests/queryObserver.test.tsx index a0d835c086..2e3458883d 100644 --- a/packages/query-core/src/tests/queryObserver.test.tsx +++ b/packages/query-core/src/tests/queryObserver.test.tsx @@ -469,7 +469,7 @@ describe('queryObserver', () => { const observer = new QueryObserver(queryClient, { queryKey: key, queryFn: fetchData, - cacheTime: 0, + gcTime: 0, refetchInterval: 10, }) const unsubscribe = observer.subscribe(() => undefined) diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index 3103e31b6e..1019202c4a 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -77,7 +77,7 @@ export interface QueryOptions< retry?: RetryValue retryDelay?: RetryDelayValue networkMode?: NetworkMode - cacheTime?: number + gcTime?: number queryFn?: QueryFunction queryHash?: string queryKey?: TQueryKey @@ -577,7 +577,7 @@ export interface MutationOptions< retry?: RetryValue retryDelay?: RetryDelayValue networkMode?: NetworkMode - cacheTime?: number + gcTime?: number _defaulted?: boolean meta?: MutationMeta } diff --git a/packages/react-query/src/__tests__/QueryClientProvider.test.tsx b/packages/react-query/src/__tests__/QueryClientProvider.test.tsx index bc1474a520..71a977acd0 100644 --- a/packages/react-query/src/__tests__/QueryClientProvider.test.tsx +++ b/packages/react-query/src/__tests__/QueryClientProvider.test.tsx @@ -113,7 +113,7 @@ describe('QueryClientProvider', () => { queryCache, defaultOptions: { queries: { - cacheTime: Infinity, + gcTime: Infinity, }, }, }) @@ -143,7 +143,7 @@ describe('QueryClientProvider', () => { await waitFor(() => rendered.getByText('test')) expect(queryCache.find({ queryKey: key })).toBeDefined() - expect(queryCache.find({ queryKey: key })?.options.cacheTime).toBe(Infinity) + expect(queryCache.find({ queryKey: key })?.options.gcTime).toBe(Infinity) }) describe('with custom context', () => { diff --git a/packages/react-query/src/__tests__/suspense.test.tsx b/packages/react-query/src/__tests__/suspense.test.tsx index 30d75fcfc2..3b7a1b53a5 100644 --- a/packages/react-query/src/__tests__/suspense.test.tsx +++ b/packages/react-query/src/__tests__/suspense.test.tsx @@ -938,7 +938,7 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => rendered.getByText('error boundary')) }) - it('should render the correct amount of times in Suspense mode when cacheTime is set to 0', async () => { + it('should render the correct amount of times in Suspense mode when gcTime is set to 0', async () => { const key = queryKey() let state: UseQueryResult | null = null @@ -956,7 +956,7 @@ describe("useQuery's in Suspense mode", () => { return count }, suspense: true, - cacheTime: 0, + gcTime: 0, }) return ( diff --git a/packages/react-query/src/__tests__/useMutation.test.tsx b/packages/react-query/src/__tests__/useMutation.test.tsx index 9f15b2e262..fccd24082a 100644 --- a/packages/react-query/src/__tests__/useMutation.test.tsx +++ b/packages/react-query/src/__tests__/useMutation.test.tsx @@ -860,7 +860,7 @@ describe('useMutation', () => { return count }, mutationKey, - cacheTime: 0, + gcTime: 0, onSuccess, onSettled, }) diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx index 96ac6a1de9..09ecbad372 100644 --- a/packages/react-query/src/__tests__/useQuery.test.tsx +++ b/packages/react-query/src/__tests__/useQuery.test.tsx @@ -818,7 +818,7 @@ describe('useQuery', () => { expect(states[1]).toMatchObject({ data: 'data' }) }) - it('should pick up a query when re-mounting with cacheTime 0', async () => { + it('should pick up a query when re-mounting with gcTime 0', async () => { const key = queryKey() const states: UseQueryResult[] = [] @@ -845,7 +845,7 @@ describe('useQuery', () => { return 'data: ' + value }, - cacheTime: 0, + gcTime: 0, notifyOnChangeProps: 'all', }) states.push(state) @@ -891,7 +891,7 @@ describe('useQuery', () => { }) }) - it('should not get into an infinite loop when removing a query with cacheTime 0 and rerendering', async () => { + it('should not get into an infinite loop when removing a query with gcTime 0 and rerendering', async () => { const key = queryKey() const states: UseQueryResult[] = [] @@ -905,7 +905,7 @@ describe('useQuery', () => { return 'data' }, - cacheTime: 0, + gcTime: 0, notifyOnChangeProps: 'all', }) @@ -4009,14 +4009,14 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('status: loading, idle')) }) - test('should not schedule garbage collection, if cacheTimeout is set to `Infinity`', async () => { + test('should not schedule garbage collection, if gcTimeout is set to `Infinity`', async () => { const key = queryKey() function Page() { const query = useQuery({ queryKey: key, queryFn: () => 'fetched data', - cacheTime: Infinity, + gcTime: Infinity, }) return
{query.data}
} @@ -4024,12 +4024,41 @@ describe('useQuery', () => { const rendered = renderWithClient(queryClient, ) await waitFor(() => rendered.getByText('fetched data')) + jest.useFakeTimers('legacy') + const setTimeoutSpy = jest.spyOn(globalThis.window, 'setTimeout') rendered.unmount() - const query = queryCache.find({ queryKey: key }) - // @ts-expect-error - expect(query!.cacheTimeout).toBe(undefined) + expect(setTimeoutSpy).not.toHaveBeenCalled() + jest.useRealTimers() + }) + + test('should schedule garbage collection, if gcTimeout is not set to infinity', async () => { + const key = queryKey() + + function Page() { + const query = useQuery({ + queryKey: key, + queryFn: () => 'fetched data', + gcTime: 1000 * 60 * 10, //10 Minutes + }) + return
{query.data}
+ } + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => rendered.getByText('fetched data')) + + jest.useFakeTimers('legacy') + const setTimeoutSpy = jest.spyOn(globalThis.window, 'setTimeout') + + rendered.unmount() + + expect(setTimeoutSpy).toHaveBeenLastCalledWith( + expect.any(Function), + 1000 * 60 * 10, + ) + jest.useRealTimers() }) it('should not cause memo churn when data does not change', async () => { diff --git a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx index d39848d0f2..a6cd97a235 100644 --- a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx +++ b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx @@ -111,7 +111,7 @@ describe('QueryClientProvider', () => { queryCache, defaultOptions: { queries: { - cacheTime: Infinity, + gcTime: Infinity, }, }, }) @@ -141,7 +141,7 @@ describe('QueryClientProvider', () => { await waitFor(() => screen.getByText('test')) expect(queryCache.find({ queryKey: key })).toBeDefined() - expect(queryCache.find({ queryKey: key })?.options.cacheTime).toBe(Infinity) + expect(queryCache.find({ queryKey: key })?.options.gcTime).toBe(Infinity) }) describe('with custom context', () => { diff --git a/packages/solid-query/src/__tests__/createMutation.test.tsx b/packages/solid-query/src/__tests__/createMutation.test.tsx index b13a03606d..734176a080 100644 --- a/packages/solid-query/src/__tests__/createMutation.test.tsx +++ b/packages/solid-query/src/__tests__/createMutation.test.tsx @@ -955,7 +955,7 @@ describe('useMutation', () => { return count }, mutationKey: mutationKey, - cacheTime: 0, + gcTime: 0, onSuccess, onSettled, })) diff --git a/packages/solid-query/src/__tests__/createQuery.test.tsx b/packages/solid-query/src/__tests__/createQuery.test.tsx index 49eba723bf..c1d1400619 100644 --- a/packages/solid-query/src/__tests__/createQuery.test.tsx +++ b/packages/solid-query/src/__tests__/createQuery.test.tsx @@ -862,7 +862,7 @@ describe('createQuery', () => { expect(states[1]).toMatchObject({ data: 'data' }) }) - it('should pick up a query when re-mounting with cacheTime 0', async () => { + it('should pick up a query when re-mounting with gcTime 0', async () => { const key = queryKey() const states: CreateQueryResult[] = [] @@ -891,7 +891,7 @@ describe('createQuery', () => { await sleep(10) return 'data: ' + value }, - cacheTime: 0, + gcTime: 0, })) createRenderEffect(() => { states.push({ ...state }) @@ -3959,14 +3959,14 @@ describe('createQuery', () => { await waitFor(() => screen.getByText('status: loading, idle')) }) - it('should not schedule garbage collection, if cacheTimeout is set to `Infinity`', async () => { + it('should not schedule garbage collection, if gcTimeout is set to `Infinity`', async () => { const key = queryKey() function Page() { const query = createQuery(() => ({ queryKey: key, queryFn: () => 'fetched data', - cacheTime: Infinity, + gcTime: Infinity, })) return
{query.data}
} @@ -3978,12 +3978,40 @@ describe('createQuery', () => { )) await waitFor(() => screen.getByText('fetched data')) + const setTimeoutSpy = jest.spyOn(window, 'setTimeout') result.unmount() - const query = queryCache.find({ queryKey: key }) - // @ts-expect-error - expect(query!.cacheTimeout).toBe(undefined) + expect(setTimeoutSpy).not.toHaveBeenCalled() + }) + + it('should schedule garbage collection, if gcTimeout is not set to `Infinity`', async () => { + const key = queryKey() + + function Page() { + const query = createQuery(() => ({ + queryKey: key, + queryFn: () => 'fetched data', + gcTime: 1000 * 60 * 10, //10 Minutes + })) + return
{query.data}
+ } + + const result = render(() => ( + + + + )) + + await waitFor(() => screen.getByText('fetched data')) + const setTimeoutSpy = jest.spyOn(window, 'setTimeout') + + result.unmount() + + expect(setTimeoutSpy).toHaveBeenLastCalledWith( + expect.any(Function), + 1000 * 60 * 10, + ) }) it('should not cause memo churn when data does not change', async () => { diff --git a/packages/solid-query/src/__tests__/suspense.test.tsx b/packages/solid-query/src/__tests__/suspense.test.tsx index aaa5fd480b..907a20d968 100644 --- a/packages/solid-query/src/__tests__/suspense.test.tsx +++ b/packages/solid-query/src/__tests__/suspense.test.tsx @@ -954,7 +954,7 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => screen.getByText('error boundary')) }) - it('should render the correct amount of times in Suspense mode when cacheTime is set to 0', async () => { + it('should render the correct amount of times in Suspense mode when gcTime is set to 0', async () => { const key = queryKey() let state: CreateQueryResult | null = null @@ -969,7 +969,7 @@ describe("useQuery's in Suspense mode", () => { await sleep(10) return count }, - cacheTime: 0, + gcTime: 0, })) createRenderEffect( diff --git a/packages/vue-query/src/__mocks__/useQueryClient.ts b/packages/vue-query/src/__mocks__/useQueryClient.ts index cd7484f8ba..65645ef45c 100644 --- a/packages/vue-query/src/__mocks__/useQueryClient.ts +++ b/packages/vue-query/src/__mocks__/useQueryClient.ts @@ -8,7 +8,7 @@ const queryClient = new QueryClient({ }, }, defaultOptions: { - queries: { retry: false, cacheTime: Infinity }, + queries: { retry: false, gcTime: Infinity }, }, })