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.

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