I'm migrating a legacy codebase from saga (no cache) to react-query. My starting point is to replicate exactly as we have now, a very unoptimised approach, and optimise later, enabling cache piece by piece. I have the following immediate requirements:
- I have no interest in loading stale data in the background.
- I do not want cache by default
- Every
useQuery
mount should refetch the data, loading as usual - I can enable the cache for individual queries
This is my query client:
client = new QueryClient({
defaultOptions: { queries: { retry: false, staleTime: Infinity, cacheTime: 0 } },
});
I've written a wrapper around this which mimics our old API with the query fn, for migration sake, which looks like this:
export const useRequest = (
url,
opts,
) => {
return result = useQuery({
queryKey: [url],
cacheTime: 0,
...opts,
queryFn: () => request(url, 'GET'),
});
};
I've written unit tests around this to ensure cache is disabled, which are:
const { result: hook1, waitFor } = renderHook(() => useRequest('/jobs'), {
wrapper,
});
await waitFor(() => expect(hook1.current.isSuccess).toBe(true));
// Hook 2 load
const { result: hook2 } = renderHook(() => useRequest('/jobs'), {
wrapper,
});
await waitFor(() => expect(hook2.current.isFetching).toBe(false));
// Assert cache
await waitFor(() => expect(handler).toBeCalledTimes(2));
Handler being a spy function around my API test harness.
Unfortunately, this is failing, and on debugging, it is loading data from the cache.
With a cache time of 0, and a stale time of Infinity, why is this loading data from the cache? I was under the impression a cache time of 0 would always invalidate the cache immediately.
I can fix this by removing staleTime: Infinity
. However, this fails my last requirement.
Consider this second test, which ensures that if I enable the cache, my API does not get hit twice.
// Hook 1 load
const { result: hook1, waitFor } = renderHook(() => useRequest('/jobs', { cacheTime: 1000 }), {
wrapper,
});
await waitFor(() => expect(hook1.current.isSuccess).toBe(true));
// Hook 2 load
const { result: hook2 } = renderHook(() => useRequest('/jobs'), {
wrapper,
});
// Stale time will refetch in background
await waitFor(() => expect(hook2.current.isFetching).toBe(false));
// Assert cache
await waitFor(() => {
expect(handler).toBeCalledTimes(1);
});
This fails if I remove staleTime
, as naturally, the data will be stale and will refetch in the background.
My understanding is, if cacheTime
is 0, then staleTime
should not matter, as cache gets cleared out immediately. I've read all the docs I can to understand this, but I cannot seem to figure out why it behaves this way.
Could someone explain why the first test fails and loads from the cache, when cacheTime
is 0?