Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

46 linesCodeBlameRaw
1import { useEffect, useRef } from "react";
2import { useRevalidator } from "react-router";
3
4/** The wait before the error page's try `attempt` (from 1): 3 s, 6 s, 12 s, then every 30 s. */
5export function reconnectDelay(attempt: number): number {
6 return Math.min(3000 * 2 ** (attempt - 1), 30_000);
7}
8
9/**
10 * While `on` (the page fell to its error page because the network was
11 * gone), loads the page again: a little later each time, at once when the
12 * browser says it is back online or the tab comes back into view. Once a
13 * load arrives, the router drops the error and the page is back.
14 */
15export function useReconnect(on: boolean) {
16 const revalidator = useRevalidator();
17 const latest = useRef(revalidator);
18 latest.current = revalidator;
19 useEffect(() => {
20 if (!on) return;
21 let attempt = 0;
22 let timer: ReturnType<typeof setTimeout> | undefined;
23 const schedule = () => {
24 clearTimeout(timer);
25 timer = setTimeout(retry, reconnectDelay(++attempt));
26 };
27 const retry = () => {
28 if (document.visibilityState === "visible" && latest.current.state === "idle") void latest.current.revalidate();
29 schedule();
30 };
31 const now = () => {
32 if (document.visibilityState !== "visible") return;
33 attempt = 0;
34 clearTimeout(timer);
35 retry();
36 };
37 schedule();
38 window.addEventListener("online", now);
39 document.addEventListener("visibilitychange", now);
40 return () => {
41 clearTimeout(timer);
42 window.removeEventListener("online", now);
43 document.removeEventListener("visibilitychange", now);
44 };
45 }, [on]);
46}