T
TanStack10mo ago
multiple-amethyst

Stop refetch on page change

Hey guys, I'm using Next 14, I have 2 pages. I use react-query as source of truth in my app whether user is signed in or not, basically I have a session http only cookie and on initial web app request I make another to my BE to ask for user info. All is great when I am signed in, but when I'm not, every time I change the pages in my app react-query tries to refetch the user data which obviously fails. On top of that I'm doing next server prefetching so most of the time if user is signed in the data, will be there on first page load. If he is not signed in or for some reason my prefetch failed, I understand and ofc would love to have one more try on the web, but only one. I tried to pass every refetchOn* prop with false to the hook but it doesn't work. Here is SS of the related code: PS: the useUser hook is used in most of my components, including the ones used on the SS, if that changes anything :x
No description
15 Replies
multiple-amethyst
multiple-amethystOP10mo ago
staleTime: Infinity didn't help as well
stormy-gold
stormy-gold10mo ago
Just from your description, can you set disabled to true, when you're not signed in? https://tanstack.com/query/latest/docs/framework/react/guides/disabling-queries#disablingpausing-queries
multiple-amethyst
multiple-amethystOP10mo ago
@Deleted User I don't think so, I mean as I said, this hook is literally the source of truth, there is nothing I can pass to it to conclude whether I am signed in, this hook is literally the info
stormy-gold
stormy-gold10mo ago
every time I change the pages in my app
Does this mean redirect? If you're doing something that destroys memory it will "start over"
multiple-amethyst
multiple-amethystOP10mo ago
@troywoy Just simple Next Link components:
<Link
href="/"
className={cn(
"border border-transparent px-4 py-2 text-[#676055] transition-all duration-300",
pathname === "/" && activeClassName,
)}
>
Home Page
</Link>
<Link
href="/leaderboard"
className={cn(
"border border-transparent px-4 py-2 text-[#676055] transition-all duration-300",
pathname === "/leaderboard" && activeClassName,
)}
>
Leaderboard
</Link>
<Link
href="/"
className={cn(
"border border-transparent px-4 py-2 text-[#676055] transition-all duration-300",
pathname === "/" && activeClassName,
)}
>
Home Page
</Link>
<Link
href="/leaderboard"
className={cn(
"border border-transparent px-4 py-2 text-[#676055] transition-all duration-300",
pathname === "/leaderboard" && activeClassName,
)}
>
Leaderboard
</Link>
stormy-gold
stormy-gold10mo ago
You mention server side prefetch so I assume you have hydration correctly set up? Using the DevTools would be wise here as staleTime: Infinity should give you the result you want. Perhaps it’s getting garbage collected so when it mounts again there’s nothing in the cache
multiple-amethyst
multiple-amethystOP10mo ago
@troywoy I believe prefetch is set up correctly but it doesn't really matter -it happens only once in layout, never again in app live. Even when I remove it entirely just for the sake of argument, it doesn't change the outcome. Yes I believe the staleTime: Infinity should do the trick, but it doesn't. Currently I commented all my code in pages and simple call the hook with staleTime: Infinity. Still refetch on every page change. I can see in the RQ DevTools that the state changes from stale to fetching on every page change. I don't understand why, maybe it's a bug? ss:
No description
multiple-amethyst
multiple-amethystOP10mo ago
Found it! There is a retryOnMount: false prop not the refetchOnMount! Which makes sense from the name, missed it in this huge collection of props with similar name 😄 Thanks fot the help! ❤️
stormy-gold
stormy-gold10mo ago
Not really much help lol, but good to know retryOnMount has a different ruleset (ie, your failed query was retried regardless of staleTime)
multiple-amethyst
multiple-amethystOP10mo ago
yup
multiple-amethyst
multiple-amethyst10mo ago
adding this should solve the issue - refetchOnWindowFocus: false,
like-gold
like-gold10mo ago
Yes I believe the staleTime: Infinity should do the trick, but it doesn't.
it doesn't because you have no data. If you let the query fail by returning a rejected promise, it will be in error state without data. If you then have a component that wants to read that data, we'll try to get it again. That's on purpose. maybe this mental model helps: "queries in error state are always stale" I'm wondering why you don't just redirect to the login page or something if the user tries to see a page that they shouldn't be able to see when they are not logged in?
extended-yellow
extended-yellow10mo ago
Im having the same issue, figured I might ask here instead of making a new thread. Ive set some prefetch queries to fetch both the next and previous pages this works fine according to my backend logs. Upon requesting
/api/v1/products?page=1
/api/v1/products?page=1
theres a request for
/api/v1/products?page=2
/api/v1/products?page=2
both with a status of 200. However on the client side. Navigating to
/api/v1/products?page=2
/api/v1/products?page=2
I see a loading spinner. Ive set
staleTime: 60 * 1000
staleTime: 60 * 1000
and
retryOnMount: false
retryOnMount: false
still the same issue. Any suggestions?
like-gold
like-gold10mo ago
can't really say without a minimal reproduction
extended-yellow
extended-yellow10mo ago
Alright. Ive got a hook called useProducts which fetches all products matching the provided filter and page from my database
export function useProducts() {
const queryClient = useQueryClient();
const { category } = useParams();
const [searchParams] = useSearchParams();
const filter = searchParams.get("manufacturer");

const page = Number(searchParams.get("page")) || 1;

const { isLoading, data: { data: products, count, brands } = {} } = useQuery({
queryFn: () => getProducts(page, category, filter),
queryKey: ["products", page, category, filter],
});

// Prefetching
const pageCount = Math.ceil(count / PAGE_SIZE);

if (page < pageCount) {
queryClient.prefetchQuery({
queryFn: () => getProducts(page + 1, category, filter),
queryKey: ["products", category, filter, page + 1],
});
}

if (page > 1) {
queryClient.prefetchQuery({
queryFn: () => getProducts(page - 1, category, filter),
queryKey: ["products", category, filter, page - 1],
});
}

return { isLoading, products, count, brands };
}
export function useProducts() {
const queryClient = useQueryClient();
const { category } = useParams();
const [searchParams] = useSearchParams();
const filter = searchParams.get("manufacturer");

const page = Number(searchParams.get("page")) || 1;

const { isLoading, data: { data: products, count, brands } = {} } = useQuery({
queryFn: () => getProducts(page, category, filter),
queryKey: ["products", page, category, filter],
});

// Prefetching
const pageCount = Math.ceil(count / PAGE_SIZE);

if (page < pageCount) {
queryClient.prefetchQuery({
queryFn: () => getProducts(page + 1, category, filter),
queryKey: ["products", category, filter, page + 1],
});
}

if (page > 1) {
queryClient.prefetchQuery({
queryFn: () => getProducts(page - 1, category, filter),
queryKey: ["products", category, filter, page - 1],
});
}

return { isLoading, products, count, brands };
}
And Ive got a simple Pagination component that has a few buttons that change the searchParam "page" to +1 or -1, however in // Prefetching Ive made it prefetch both the next page and previous page. This works fine, my backend logs confirm this as both requests for the next and previous page can be seen in my logs with a green 200 status code. But opon navigating to the next or previous page. I am met with the loader which is set to display if isLoading is true, which should not be the case as Ive prefetched the data. Anything more you need to understand? No way I figured it out And I can say Im blind as a bat queryKeys didnt match queryKey: ["products", page, category, filter] compared to the prefetch query queryKey: ["products", category, filter, page + 1]

Did you find this page helpful?