-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to get loading state when using useDeferredValue
with useReadQuery
?
#12227
Comments
useDeferredValue
with useReadQuery
to stop showing Suspense fallback?useDeferredValue
with useReadQuery
?
Hey @Temzasse 👋 I'll do my best to dig into your reproduction a bit further when I have time (unless @phryneas beats me to it). A quick note though, I'm curious if |
Well I'll be damned. I clearly was way too deep into this topic and couldn't see the obvious simple solution 🙈 Your suggestion works nicely: const { queryRef } = Route.useLoaderData();
const deferredQueryRef = useDeferredValue(queryRef);
const { data } = useReadQuery(deferredQueryRef);
const isPending = queryRef !== deferredQueryRef; // 🎉🎉🎉 The only minor UX issue is that the import { useSpinDelay } from 'spin-delay';
const isPending = useSpinDelay(queryRef !== deferredQueryRef); @jerelmiller feel free to close this issue. Would it be possible to have a mention about this |
@Temzasse glad that worked! A bit of a bummer you have to do it that way, but I can understand the tradeoff that TanStack Router made here. A mention about |
I'm def not an expert at writing docs but here are my 2 cents: In think most people don't yet fully understand how Maybe it's just my experience but my challenges with Suspense have always revolved around URL state that drives the query variables. I initially struggled with this same issue with React Query and wrote about it on my blog. I think it's great that you mention I totally get that having library/framework specific examples in the docs can be quite challenging to produce and maintain but I think most frameworks, Next.js / Tanstack / Remix / React Router, start to have very similar patterns and hooks that could be generally documented. For example Hopefully that helps 🤓 |
Yep this is very helpful! Appreciate the feedback! Its always tricky to find the right balance between going into general Suspense concepts vs Apollo Client's specific integration for it. We'd rather rely on the React docs to do most of the teaching on Suspense itself (since those docs are wayyyyy better than anything we could put out). We've leaned toward showing the integration aspect while hoping readers would go to the React docs to learn more about the general Suspense concepts. We are also trying not to be too prescriptive from our end as we want devs to feel like they have the flexibility to do what works best for them. I think I avoided the Thanks again for the discussion! Hopefully this issue helps someone else in the future 🙂 |
Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo Client usage and allow us to serve you better. |
I'm encountering a similar issue in my project, but with React Router v7 instead of TanStack. I tried adapting your example, but the behavior isn't quite the same. In our case, when the search params change, we receive a new When this new value is passed to Do you have any insights into what might be different in my approach compared to yours that could be causing this issue? const query = useLoaderData<PreloadedQueryRef<CampaignOverviewQuery>>();
const deferredQuery = useDeferredValue(query); // is always the same as query even when the query is refetching with new variables
const { data } = useReadQuery(deferredQuery); // will block rerender until new data is loaded
const isRefetching = query !== deferredQuery;
console.log('isRefetching', isRefetching); // is always false here |
@MickJasker React Router wraps route transitions in |
I found this discussion with this explanation:
If I'm reading this right, I'm guessing |
This is an interesting read and makes a lot of sense to me. Unfortunately, we'll need to look for a different solution. We’ve used The issue is that React Router can’t track the query’s promise when it’s not being awaited in the loader. A flow that seems logical to me would be for |
Unfortunately this is just not possible with the way Suspense works 😞.
This is why something like |
Issue Description
Hi 👋🏻
This is a follow-up issue related to a discussion on Bluesky where I tagged @phryneas and asked a React Suspense related question.
Basically the original question was: how do we stop React from showing the Suspense fallback when using
preloadQuery
+useReadQuery
in a situation where usingstartTransition
is not feasible?During the discussion various methods were suggested but in the end
useDeferredValue
was suggested as a potential solution, for example:With this approach there is one problem tho, how do we get the loading state after the initial suspension?
The context for this issue is that with Tanstack Router it seems to be impossible/impractical to use
startTransition
in conjunction with useNavigate'snavigate
function (to update the search params) due to the way Tanstack Router uses useSyncExternalStore internally. See the following quote from one of the Tanstack maintainers:We tried using
preloadQuery
anduseReadQuery
in the following way with Tanstack Router'sloader
method which receives the query variables from the search params managed by Tanstack Router:With this code whenever the search params change the Suspense fallback is briefly shown even though the call to
navigate
is wrapped instartTransition
. This limitation is not really an Apollo issue but it does surface a need to have a way to stop showing the Suspense fallback withoutstartTransition
. As mentioned in at start the only other way to do that is with useDeferredValue but with that approach we lose the inline pending indicator.I tried to do the following to get the pending state:
But that doesn't seem to work. I guess it's because
queryRef
contains symbols and functions which are not stable and thus always result infalse
in the equal comparison 🤔However, that same approach seems to work with
useSuspenseQuery
so we are able to somewhat circumvent this issue by not usinguseReadQuery
but instead use a deferrable version ofuseSuspenseQuery
that defers the queryvariables
in the same way Dominik hinted in his quote above:And then use it in the component:
However this is not as ergonomic DX-wise as we need to repeat the query options exactly the same way as in the preloaded query, and as mentioned in this comment by @jerelmiller there are no guarantees that Apollo won't make two network requests with this approach compared to the
useReadQuery
approach.So in summary, is there a way to use
useReadQuery
withuseDeferredValue
in a way that we can still get the loading state of the query when the variables of it change, or is there some other approach we can take to stop React from showing the Suspense fallback in situations where we are not able to usestartTransition
?PS: I couldn't find any mention of
useDeferredValue
in the Apollo Client docs so it might be a good idea to add a section about it in the Suspense page.Link to Reproduction
https://github.com/Temzasse/router-comparison/tree/main/tanstack-router
Reproduction Steps
After cloning the repo locally:
cd tanstack-router
npm install
npm run dev
http://localhost:3001/suspense
ja
orfi
in the input and submit the formisPending
state is alwaysfalse
.@apollo/client
version3.12.3
The text was updated successfully, but these errors were encountered: