Skip to content

Instantly share code, notes, and snippets.

@srph
Created March 24, 2023 16:26
Show Gist options
  • Save srph/e2bd71e52e4571b977be4d829894ead5 to your computer and use it in GitHub Desktop.
Save srph/e2bd71e52e4571b977be4d829894ead5 to your computer and use it in GitHub Desktop.
React: useCountdown hook
import { useState, useMemo } from 'react'
import { intervalToDuration, isAfter, eachDayOfInterval, addMonths } from 'date-fns'
import { useInterval } from '@/hooks'
interface UseCountdownParameters {
target: Date
}
interface UseCountdownDuration {
days: string
hours: string
minutes: string
seconds: string
}
const useCountdown = ({ target }: UseCountdownParameters): UseCountdownDuration => {
const [now, setNow] = useState(() => new Date())
useInterval(() => {
setNow(new Date())
}, 1000)
const duration: UseCountdownDuration = useMemo(() => {
if (isAfter(now, target)) {
return { days: '00', hours: '00', minutes: '00', seconds: '00' }
}
const remaining = intervalToDuration({
start: now,
end: target
})
const pad = (n: number | undefined) => {
return String(n).padStart(2, '0')
}
// We'll convert the [remaining months] to [days] then get its sum with the [remaining days]
const getNumberOfDays = (): number => {
const remainingDays = Number(remaining.days)
if (remaining.months) {
return (
eachDayOfInterval({
start: now,
end: addMonths(now, remaining.months)
}).length + remainingDays
)
}
return remainingDays
}
return {
days: pad(getNumberOfDays()),
hours: pad(remaining.hours),
minutes: pad(remaining.minutes),
seconds: pad(remaining.seconds)
}
}, [target, now])
return duration
}
export { useCountdown }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment