倒數計時器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時間(毫秒)
子元素是放置時間過期時顯示的內容
留言