mirror of
https://github.com/block/buzz.git
synced 2026-10-31 06:21:34 +00:00
64 lines
1.6 KiB
TypeScript
64 lines
1.6 KiB
TypeScript
import * as React from "react";
|
|
|
|
const DEFAULT_TIMEOUT_MS = 2_000;
|
|
|
|
/**
|
|
* Defers activation until the browser is idle (via `requestIdleCallback`) or
|
|
* a timeout elapses — whichever comes first.
|
|
*
|
|
* Pass `immediate: true` to skip deferral and return `true` on the first render
|
|
* (useful when the deferred content is already in view).
|
|
*/
|
|
export function useDeferredLoad(
|
|
options: { immediate?: boolean; timeoutMs?: number } = {},
|
|
): boolean {
|
|
const { immediate = false, timeoutMs = DEFAULT_TIMEOUT_MS } = options;
|
|
const [isReady, setIsReady] = React.useState(immediate);
|
|
|
|
React.useEffect(() => {
|
|
if (isReady) {
|
|
return;
|
|
}
|
|
|
|
if (immediate) {
|
|
setIsReady(true);
|
|
return;
|
|
}
|
|
|
|
const activate = () => {
|
|
setIsReady(true);
|
|
};
|
|
|
|
if ("requestIdleCallback" in window) {
|
|
const idleId = window.requestIdleCallback(activate, {
|
|
timeout: timeoutMs,
|
|
});
|
|
return () => {
|
|
window.cancelIdleCallback(idleId);
|
|
};
|
|
}
|
|
|
|
const timeoutId = globalThis.setTimeout(activate, timeoutMs);
|
|
return () => {
|
|
globalThis.clearTimeout(timeoutId);
|
|
};
|
|
}, [immediate, isReady, timeoutMs]);
|
|
|
|
return isReady;
|
|
}
|
|
|
|
let hasCompletedStartup = false;
|
|
|
|
/**
|
|
* Convenience wrapper: defers non-critical startup work (presence, notifications,
|
|
* subscriptions) until the main shell is interactive. Uses a module-level flag
|
|
* so the deferral only happens once per app lifecycle — remounts skip the delay.
|
|
*/
|
|
export function useDeferredStartup(): boolean {
|
|
const ready = useDeferredLoad();
|
|
if (ready) {
|
|
hasCompletedStartup = true;
|
|
}
|
|
return hasCompletedStartup || ready;
|
|
}
|