{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "useCountDown", "title": "useCountDown", "description": "useCountDown hook", "dependencies": [ "dayjs" ], "registryDependencies": [ "https://shadcn-ahooks.vercel.app/r/utils.json", "https://shadcn-ahooks.vercel.app/r/useLatest.json" ], "files": [ { "path": "packages/hooks/src/useCountDown/index.ts", "content": "import dayjs from 'dayjs';\nimport { useEffect, useMemo, useState } from 'react';\nimport useLatest from '../useLatest';\nimport { isNumber } from '../utils/index';\n\nexport type TDate = dayjs.ConfigType;\n\nexport interface Options {\n leftTime?: number;\n targetDate?: TDate;\n interval?: number;\n onEnd?: () => void;\n}\n\nexport interface FormattedRes {\n days: number;\n hours: number;\n minutes: number;\n seconds: number;\n milliseconds: number;\n}\n\nconst calcLeft = (target?: TDate) => {\n if (!target) {\n return 0;\n }\n // https://stackoverflow.com/questions/4310953/invalid-date-in-safari\n const left = dayjs(target).valueOf() - Date.now();\n return left < 0 ? 0 : left;\n};\n\nconst parseMs = (milliseconds: number): FormattedRes => {\n return {\n days: Math.floor(milliseconds / 86400000),\n hours: Math.floor(milliseconds / 3600000) % 24,\n minutes: Math.floor(milliseconds / 60000) % 60,\n seconds: Math.floor(milliseconds / 1000) % 60,\n milliseconds: Math.floor(milliseconds) % 1000,\n };\n};\n\nconst useCountdown = (options: Options = {}) => {\n const { leftTime, targetDate, interval = 1000, onEnd } = options || {};\n\n const memoLeftTime = useMemo(() => {\n return isNumber(leftTime) && leftTime > 0 ? Date.now() + leftTime : undefined;\n }, [leftTime]);\n\n const target = 'leftTime' in options ? memoLeftTime : targetDate;\n\n const [timeLeft, setTimeLeft] = useState(() => calcLeft(target));\n\n const onEndRef = useLatest(onEnd);\n\n useEffect(() => {\n if (!target) {\n // for stop\n setTimeLeft(0);\n return;\n }\n\n // 立即执行一次\n setTimeLeft(calcLeft(target));\n\n const timer = setInterval(() => {\n const targetLeft = calcLeft(target);\n setTimeLeft(targetLeft);\n if (targetLeft === 0) {\n clearInterval(timer);\n onEndRef.current?.();\n }\n }, interval);\n\n return () => clearInterval(timer);\n }, [target, interval]);\n\n const formattedRes = useMemo(() => parseMs(timeLeft), [timeLeft]);\n\n return [timeLeft, formattedRes] as const;\n};\n\nexport default useCountdown;\n", "type": "registry:file", "target": "src/hooks/ahooks/useCountDown/index.ts" } ], "type": "registry:hook" }