mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-18 00:37:32 -05:00
39 lines
1.3 KiB
TypeScript
39 lines
1.3 KiB
TypeScript
import * as React from "react";
|
|
|
|
/**
|
|
* Keeps an app shell resource route's JSON fresh, outside the router. A
|
|
* `useFetcher` load that is still in flight when a navigation starts is folded
|
|
* into that navigation and has to settle before it completes (React Router
|
|
* reruns cancelled fetcher loads without consulting `shouldRevalidate`), so a
|
|
* refresh that only feeds the app shell would hold up every page change it
|
|
* happens to overlap with.
|
|
*/
|
|
export function useBackgroundResource<T>(url: string) {
|
|
const [data, setData] = React.useState<T>();
|
|
const [isLoading, setIsLoading] = React.useState(false);
|
|
const latestRequestRef = React.useRef(0);
|
|
|
|
// stable so effects that refresh after a mutation don't re-run every render
|
|
const refresh = React.useCallback(async () => {
|
|
const requestId = ++latestRequestRef.current;
|
|
setIsLoading(true);
|
|
|
|
try {
|
|
const response = await fetch(url);
|
|
if (!response.ok) return;
|
|
|
|
const json = (await response.json()) as T;
|
|
// a newer refresh already started, its response is the fresher one
|
|
if (requestId !== latestRequestRef.current) return;
|
|
|
|
setData(json);
|
|
} catch {
|
|
// a background refresh failing just leaves the last data in place
|
|
} finally {
|
|
if (requestId === latestRequestRef.current) setIsLoading(false);
|
|
}
|
|
}, [url]);
|
|
|
|
return { data, isLoading, refresh };
|
|
}
|