倒數計時器React hooks+MUI+typescript

這篇是受Tapas Adhikary所寫的How to create a countdown timer using React Hooks啟發


將它改成typescript和MUI的版本,並調整成自身使用的元件,並修改了

1. 使用 padStart()  處理時顯示分秒的雙位數字

2. 倒數30分鐘時會改變倒數時間成紅色

3. 避免顯示出NaN的狀況




React hooks是 hooks/useCountdown.tsx

import { useLayoutEffect, useState } from 'react'

export default function useCountdown(targetDate: number) {

const [countDown, setCountDown] = useState<number>(NaN)

useLayoutEffect(() => {
const countDownDate = new Date(targetDate).getTime()
setCountDown(countDownDate - new Date().getTime())

const interval = setInterval(() => {
setCountDown(countDownDate - new Date().getTime())
}, 1000)

return () => clearInterval(interval)
}, [targetDate])

return getReturnValues(countDown)
}

const getReturnValues = (countDown: number) => {
if (isNaN(countDown) || countDown < 0) {
return [0, 0, 0, 0]
}

const days = Math.floor(countDown / (1000 * 60 * 60 * 24))
const hours = Math.floor(
(countDown % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
)
const minutes = Math.floor((countDown % (1000 * 60 * 60)) / (1000 * 60))
const seconds = Math.floor((countDown % (1000 * 60)) / 1000)

return [days, hours, minutes, seconds]
}


倒數計時器元件 components/CountdownTimer.tsx

import Typography from '@mui/material/Typography'
import Stack from '@mui/material/Stack'
import type { ReactNode } from 'react'
import ClockIcon from '@mui/icons-material/AccessTime'
import { blue, red } from '@mui/material/colors'
import useCountdown from '../hooks/useCountdown'

const PaddingZero = (v: number, len: number) => {
const s = `${v}`
if (isNaN(len) || len <= 0) return s
return s.length < len ? s.padStart(len, '0') : s
}

type DateTimeDisplayProps = {
value: number;
type: string;
isDanger?: boolean;
padTo?: number;
}

function DateTimeDisplay(
{ value, type, isDanger, padTo }: DateTimeDisplayProps
) {

return (
<Stack textAlign='center'>
<Typography
sx={{
fontSize: {
xs: 28,
md: '2rem',
lg: '3rem',
}
}}
fontWeight={600}
fontFamily='fantasy'
color={isDanger ? red[500] : blue[500]}
>{PaddingZero(value, padTo)}</Typography>
<Typography
sx={{
fontSize: {
xs: 14,
md: '1.6rem',
lg: '2rem',
}
}}
fontWeight={600}
>{type}</Typography>
</Stack>
)
}

function ShowCounter({ days, hours, minutes, seconds }) {
const isDanger = days < 1 && hours < 1 && minutes < 30
return (
<Stack
direction='row'
alignItems='center'
justifyContent='space-around'
>
<ClockIcon sx={{
color: blue[500],
fontSize: {
xs: '2rem',
lg: '3rem',
},
marginRight: {
xs: 1,
lg: 3,
}
}} />
<DateTimeDisplay value={days} type={'Days'} isDanger={isDanger} />
<Typography></Typography>
<DateTimeDisplay
value={hours}
type={'Hours'}
isDanger={isDanger}
padTo={2}
/>
<Typography></Typography>
<DateTimeDisplay
value={minutes}
type={'Mins'}
isDanger={isDanger}
padTo={2}
/>
<Typography></Typography>
<DateTimeDisplay
value={seconds}
type={'Secs'}
isDanger={isDanger}
padTo={2}
/>
</Stack>
)
}

type CountdownTimerProps = {
targetDate: number;
children?: ReactNode;
}

export default function CountdownTimer(
{ targetDate, children }: CountdownTimerProps
) {
const [days, hours, minutes, seconds] = useCountdown(targetDate)

if (days + hours + minutes + seconds <= 0) {
return children
} else {
return (
<ShowCounter
days={days}
hours={hours}
minutes={minutes}
seconds={seconds}
/>
)
}
}


倒數計時器用法範例

<CountdownTimer targetDate={info?.dueDate}>
投票時間已過 ({formatDate(info?.dueDate)})
</CountdownTimer>

targetDate是給unix時間(毫秒)

子元素是放置時間過期時顯示的內容


留言