import { useCallback, useEffect, useState } from "react"; /** * Track whether a new version of the site has been downloaded by the service * worker and is waiting to take over. * * A waiting worker means every asset of the new version is already cached, so * installing it is just `skipWaiting` followed by a reload. The reload is * triggered from the worker's `activated` state change rather than immediately, * so the page never reloads into the old version. * * Replaces the DOM based `[data-update-worker]` wiring in the site bundle, which * only works for markup that exists when the page loads. */ export function useServiceWorkerUpdate() { let [waitingWorker, setWaitingWorker] = useState(null); useEffect(() => { if (!("serviceWorker" in navigator)) return; let isCancelled = false; let cleanups: (() => void)[] = []; function watch(worker: ServiceWorker) { if (isCancelled) return; setWaitingWorker(worker); function onStateChange() { // The new worker took over, so the page can safely reload into it. if (worker.state === "activated") window.location.reload(); } worker.addEventListener("statechange", onStateChange); cleanups.push(() => worker.removeEventListener("statechange", onStateChange)); } navigator.serviceWorker.ready.then((registration) => { if (isCancelled) return; if (registration.waiting) watch(registration.waiting); function onUpdateFound() { let installing = registration.installing; if (!installing) return; function onStateChange() { if (installing?.state === "installed") watch(installing); } installing.addEventListener("statechange", onStateChange); cleanups.push(() => installing.removeEventListener("statechange", onStateChange)); } registration.addEventListener("updatefound", onUpdateFound); cleanups.push(() => registration.removeEventListener("updatefound", onUpdateFound)); }); return () => { isCancelled = true; for (let cleanup of cleanups) cleanup(); }; }, []); let installUpdate = useCallback(() => { waitingWorker?.postMessage({ action: "skipWaiting" }); }, [waitingWorker]); return { isUpdateAvailable: waitingWorker !== null, installUpdate }; }