How can I set a time limit for suspended components to be ready before displaying the fallback? Does React have any built-ins to manage this?
What I have in mind is the following sequence of events:
So far, this is possible to do with Suspense and useTransition:
const [page, setPage] = useState("/");
const [isPending, startTransition] = useTransition();
function navigate(url) {
startTransition(() => {
setPage(url);
});
}
<Button onClick={() => navigate("/otherPage")}>
Go to next page {isPending && 'Loading'}
</Button>
// Clicking this can interrupt the transition
<Button onClick={() => navigate("/")}>
Go home
</Button>
However, in some cases, /otherPage
takes a really long time to load, and I wish for it to fallback to a full-page load via Suspense fallback after 2 seconds of waiting.
How can I do this?
As I was writing the question, I was trying different approaches to solve the problem. I found a solution, but am posting the question anyways because someone else might have a better solution, or someone might find mine useful.
In the end, I wrote a hook to do the heavy-lifting by leaning onto useOptimistic
:
const useTransitionState = (initialState, timeoutMs) => {
const [internalState, setInteralState] = useState(initialState);
const [isPending, startTransition] = useTransition();
const [pendingState, setPendingState] = useOptimistic(
internalState,
(_, newState) => newState
);
useEffect(() => {
if (internalState === pendingState || !timeoutMs) return;
const timeoutId = setTimeout(() => {
setInteralState(pendingState);
}, timeoutMs);
return () => clearTimeout(timeoutId);
}, [pendingState, internalState]);
const setState = useCallback((state) => {
startTransition(() => {
setInteralState(state);
setPendingState(state);
});
});
return [internalState, setState, isPending ? pendingState : undefined];
};
Video demo: