Slava Knyazev
Slava Knyazev

Reputation: 6081

Maximum wait time for Suspense + useTransition in React

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?

Upvotes: 0

Views: 273

Answers (1)

Slava Knyazev
Slava Knyazev

Reputation: 6081

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];
};

Demo on CodeSandbox

Video demo:

Upvotes: 0

Related Questions