Skip to content
File

Blob: src/client/hooks/use-now.ts

typescript40 lines
1import { useSyncExternalStore } from "react";
2 
3const listeners = new Set<() => void>();
4let lastTick = Date.now();
5let intervalId: ReturnType<typeof setInterval> | null = null;
6 
7function ensureClock() {
8 if (intervalId != null || typeof window === "undefined") return;
9 intervalId = setInterval(() => {
10 lastTick = Date.now();
11 for (const fn of listeners) fn();
12 }, 30_000);
13}
14 
15function subscribe(fn: () => void) {
16 ensureClock();
17 listeners.add(fn);
18 return () => {
19 listeners.delete(fn);
20 if (listeners.size === 0 && intervalId != null) {
21 clearInterval(intervalId);
22 intervalId = null;
23 }
24 };
25}
26 
27function snapshot() {
28 return lastTick;
29}
30 
31function serverSnapshot() {
32 return 0;
33}
34 
35/** Returns a timestamp that ticks once every 30 seconds — safe for "is expired"
36 * badges and relative-time labels without making the render impure. */
37export function useNow(): number {
38 return useSyncExternalStore(subscribe, snapshot, serverSnapshot);
39}