"use client" import { useSyncExternalStore } from "react" import { getDelayUntilNextMinute } from "@/features/home/lib/date-time" type Listener = () => void const listeners = new Set() let currentTime: Date | null = null let timeoutId: number | undefined function emitCurrentTime(): Date { const nextTime = new Date() currentTime = nextTime listeners.forEach((listener) => listener()) return nextTime } function scheduleNextTick() { const observedTime = emitCurrentTime() // 每次 tick 都重新对齐;标签页休眠或主线程阻塞后不会把延迟永久带入后续分钟。 timeoutId = window.setTimeout( scheduleNextTick, getDelayUntilNextMinute(observedTime), ) } function startClock() { scheduleNextTick() } function stopClock() { if (timeoutId !== undefined) window.clearTimeout(timeoutId) timeoutId = undefined currentTime = null } function subscribe(listener: Listener) { listeners.add(listener) if (listeners.size === 1) startClock() return () => { listeners.delete(listener) if (listeners.size === 0) stopClock() } } function getSnapshot() { return currentTime } function getServerSnapshot() { return null } export function useClock(): Date | null { // 服务端与 hydration 首帧固定为空;订阅建立后再读取浏览器时间,避免首屏不一致。 return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) }