I'm trying to prefetch some queries in App.getInitialProps (nextJS SSR). my assumption is, that even when the query is prefetched on SSR the default behaviors like refetchOnWindowFocus should be working as w/o any pre-fetching.
my _app.js
import { ReactQueryDevtools } from "react-query-devtools";
import { ReactQueryCacheProvider, makeQueryCache } from "react-query";
import { Hydrate, dehydrate } from "react-query/hydration";
import { getPosts } from "../hooks/usePosts";
const MyApp = ({ Component, pageProps }) => {
return (
<ReactQueryCacheProvider>
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
<ReactQueryDevtools initialIsOpen position="bottom-left" />
</Hydrate>
</ReactQueryCacheProvider>
);
};
MyApp.getInitialProps = async () => {
const queryCache = makeQueryCache();
await queryCache.prefetchQuery("posts", getPosts);
return { pageProps: { dehydratedState: dehydrate(queryCache) } };
};
export default MyApp;
my fetcher/hook:
import { useQuery } from "react-query";
import axios from "axios";
export const getPosts = async () => {
const { data } = await axios.get(
"https://jsonplaceholder.typicode.com/posts"
);
return data;
};
export default function usePosts() {
return useQuery("posts", getPosts);
}
I've created a codesandbox: https://codesandbox.io/s/distracted-pond-h45px
the posts are not refetched when the window gains focus again.
Also, when trying to set some queryConfigs like staleTime, then these queryConfig is not used, devtools are still showing staleTime=0:
export default function usePosts() {
return useQuery("posts", getPosts, { staleTime: 30 });
}
Also trying to set the staleTime as config on prefetchQuery does not work:
MyApp.getInitialProps = async () => {
const queryCache = makeQueryCache();
await queryCache.prefetchQuery("posts", getPosts, {staleTime: 30});
return { pageProps: { dehydratedState: dehydrate(queryCache) } };
};
I think this is related to https://github.com/tannerlinsley/react-query/discussions/962#discussioncomment-61332
There are a few things going on here:
- Options like staleTime and refetchOnWindowFocus are hook specific.
Ok - thx for clarifying, that's not reflected in the docs (https://react-query.tanstack.com/docs/api#querycacheprefetchquery) and also TS typing are misleading
- The query is stale, but devtools is not showing it (needs to be fixed)
Should I open an issue in the devtools project for further tracking?
Could you verify if the issue is fixed with version 2.15.1? Also created a PR in the devtools repo to fix the query status. Maybe the prefetchQuery method should indeed not accept all query options but only those which are applicable.
Hi - thanks a lot for your efforts! - I can confirm that refetchOnWindowFocus is executed when using 2.15.1.
But I've an follow-up example: https://codesandbox.io/s/black-monad-gc4gt:
My assumption here is, that due to the following queryConfig:
staleTime: Number.Infinity,
cacheTime: Number.Infinity,
enabled: false
the queryData in cache, that's prefetch via SSR will stay in the cache forever and will not be refetched on the client-side.
My observation here is:
Great! What happens if you replace Number.Infinity with Infinity?
@alfrescian The reason this is happening is that the prefetchQuery you are doing in getInitialProps is on a separate (new per route change) queryCache from the cache where the data is currently cached on the client. If you imagine this with getServerSideProps or getStaticProps that would be a bit clearer, since those are run on the server, but with getInitialProps it's a bit trickier since it can run either on the server or on the client.
I think there are ways around this that aren't documented right now (partly because Next.js isn't recommending getInitialProps anymore). In getInitialProps inside of _app.js you can do some setup and pass along extra information to the getInitialProps from the pages. In there you could _either_ use the existing queryCache if one already exists on the client, _or_ create a new one if running on the server (or first render on client). I haven't tried this myself with Next, but I have done the same approach in a private project that uses a very similar structure.
Since that part is working as intended and the original issue is resolved, I'll go ahead and close this issue. Big thanks for reporting it and feel free to open new ones if you find more things! 💯
Edit: Oh and just to be clear, I'm not saying we shouldn't have docs and/or an example for getInitialProps, a lot of people are still using it, just explaining why it wasn't a priority. 😄 Please do share if you figure out a good way to do it!
@Ephem thanks for taking the time to clarify and pointing me to my mistake to re-create the queryCache on page change.
If anyone else is struggling with it, that's my quick and simple solution approach if you'd like to prevent pre-fetching of queries in getInitialProps that are already in your cache (and not yet stale):
import React, {
FC, useEffect,
} from 'react';
import { DehydratedState, Hydrate as ReactQueryHydrate } from 'react-query/hydration';
import { useQueryCache } from 'react-query';
interface HydrateProps {
state: DehydratedState;
}
const Hydrate:FC<HydrateProps> = ({ state, children }) => {
const queryCache = useQueryCache();
useEffect(() => {
window.__QUERY_CACHE__ = queryCache;
}, [queryCache]);
return <ReactQueryHydrate state={state}>{children}</ReactQueryHydrate>;
};
export default Hydrate;
window.__QUERY_CACHE__ in getInitialProps when running on client side and only prefetch Queries that are not present or stale const queryCache = isServer ? makeQueryCache() : (window.__QUERY_CACHE__ ?? makeQueryCache());
const cachedQueries = queryCache.getQueries()
.filter((it) => it.state.isSuccess)
.map((it) => it.queryKey[0]);
const prefetchQueryKeys = [myKeys].filter((key) => !cachedQueries.includes(key));
here's an updated codesandbox: https://codesandbox.io/s/prevent-prefetch-if-cached-pkumj?file=/pages/_app.js
Most helpful comment
@Ephem thanks for taking the time to clarify and pointing me to my mistake to re-create the queryCache on page change.
If anyone else is struggling with it, that's my quick and simple solution approach if you'd like to prevent pre-fetching of queries in getInitialProps that are already in your cache (and not yet stale):
window.__QUERY_CACHE__in getInitialProps when running on client side and only prefetch Queries that are not present or stalehere's an updated codesandbox: https://codesandbox.io/s/prevent-prefetch-if-cached-pkumj?file=/pages/_app.js