T
TanStack•8mo ago
adverse-sapphire

Bypass pendingMs for certain links

I'm looking for a way to bypass pendingMs, i.e. never fall back to a suspense boundary, for certain links. Our use case is that we have a page with lots of filters and knobs for the user to tweak the data that is displayed on the page. When clicking any of the links that apply those filters (as search params), we never want the page to suspend. However, we don't want to set a really high pendingMs for that page either just to avoid it, because then the page will not show it's loading state on entry, which we do want. So basically we're looking for this behaviour: * When navigating to that page, fall back pendingComponent until the data has loaded * When navigating within that page (applying search params), never fall back to pendingComponent Can this be achieved? I haven't found any mechanism that would allow me this level of control.
7 Replies
mute-gold
mute-gold•8mo ago
If you are using query with router you can use something like this: https://www.teemutaskula.com/blog/exploring-query-suspense
Exploring using Suspense with React Query
Learn how I explored using Suspense with React Query and what issues I encountered and how I solved them.
adverse-sapphire
adverse-sapphireOP•8mo ago
No, we aren't using React Query. We're using Relay actually, but shouldn't really matter much, it's TSR that controls suspense in this case
mute-gold
mute-gold•8mo ago
Gotcha, the hook in the article is nice because it will suspend to TSR pending component on first load and the on subsequent loads it won't suspend but instead you get an isSuspending prop. Even if you aren't using TSQ, you could potentially still use the same API's used in the article. I've just never tried it without query since the suspense would be triggered in the loader rather than in the component
adverse-sapphire
adverse-sapphireOP•8mo ago
Hmm what parts exactly? The article seems to assume control over transitions, but with TSR transitions are started within the Link-component/navigate-function, so we can't really implement it like that
mute-gold
mute-gold•8mo ago
I guess I've never used relay. Are you doing all your data loading in the loader? Or is this in the component? It looks like (https://relay.dev/docs/guided-tour/rendering/loading-states/#queries) you could do something like this:
import { useDeferredValue } from "react";
import { useDeepCompareMemo } from "use-deep-compare";

export const Route = createFileRoute('/pokemon/')({
validateSearch: ...,
component: RouteComponent,
pendingComponent: RoutePendingComponent,
});

function RouteComponent() {
const { limit, offset} = Route.useSearch();
const navigate = useNavigate({ from: Route.fullPath });

const queryRef = //However this is formed using the search params in the context or relay. In this context limit and offset

const deepComparedQueryRef = useDeepCompareMemo(
() => queryRef,
[queryRef]
);

const deferredQueryRef = useDeferredValue(deepComparedQueryRef);

const isSuspending = deepComparedQueryRef !== deferredQueryRef;

const data = usePreloadedQuery(
graphql`...`,
deferredQueryKey ,
);

return (
<>
<Button
onClick={() => {
navigate({
search: (prev) => ({
...prev,
offset: offset + 1,
}),
});
}}
>
Next
</Button>

{isSuspending && (
<>
<p>Fetching New Results but not showing suspense. Would still show data below...</p>
</>
)}

//Data available from Relay
</>
);
}

function RoutePendingComponent() {
return <>Loading List...</>;
}
import { useDeferredValue } from "react";
import { useDeepCompareMemo } from "use-deep-compare";

export const Route = createFileRoute('/pokemon/')({
validateSearch: ...,
component: RouteComponent,
pendingComponent: RoutePendingComponent,
});

function RouteComponent() {
const { limit, offset} = Route.useSearch();
const navigate = useNavigate({ from: Route.fullPath });

const queryRef = //However this is formed using the search params in the context or relay. In this context limit and offset

const deepComparedQueryRef = useDeepCompareMemo(
() => queryRef,
[queryRef]
);

const deferredQueryRef = useDeferredValue(deepComparedQueryRef);

const isSuspending = deepComparedQueryRef !== deferredQueryRef;

const data = usePreloadedQuery(
graphql`...`,
deferredQueryKey ,
);

return (
<>
<Button
onClick={() => {
navigate({
search: (prev) => ({
...prev,
offset: offset + 1,
}),
});
}}
>
Next
</Button>

{isSuspending && (
<>
<p>Fetching New Results but not showing suspense. Would still show data below...</p>
</>
)}

//Data available from Relay
</>
);
}

function RoutePendingComponent() {
return <>Loading List...</>;
}
Loading States with Suspense | Relay
Relay guide to loading states
mute-gold
mute-gold•8mo ago
For reference I'm using the hook in the article and it works great for data fetching using suspense so figured this might point to some solution, but we are using TSQ so if this doesn't really help in the context of relay then someone else might have a different solution in here.
adverse-sapphire
adverse-sapphireOP•8mo ago
Yeah it's definitely possible to work around by using different data loading mechanisms, but if possible I'd like to avoid introducing the extra complexity and stick to the TSR native way of loading data. But thanks for putting in the effort of trying to come up with alternative solutions 🙂 I'd really like to know if it's possible to do this natively with TSR

Did you find this page helpful?