55 lines
1.4 KiB
TypeScript
55 lines
1.4 KiB
TypeScript
import clsx from 'clsx';
|
|
import { useEffect, useReducer } from 'react';
|
|
|
|
type State = {
|
|
hours: number;
|
|
minutes: number;
|
|
seconds: number;
|
|
};
|
|
|
|
type Action = {
|
|
type: string;
|
|
payload: Partial<State>;
|
|
};
|
|
|
|
function reducer(state: State, action: Action): State {
|
|
switch (action.type) {
|
|
case 'SET_TIME_REMAINING':
|
|
return { ...state, ...action.payload };
|
|
default:
|
|
return state;
|
|
}
|
|
}
|
|
|
|
export function Timer({ targetDate, className }: { targetDate: Date; className?: string }) {
|
|
const [timeRemaining, dispatch] = useReducer(reducer, {
|
|
hours: 0,
|
|
minutes: 0,
|
|
seconds: 0
|
|
});
|
|
|
|
targetDate = new Date(targetDate);
|
|
|
|
useEffect(() => {
|
|
const intervalId = setInterval(() => {
|
|
const timeDifference = targetDate.getTime() - Date.now();
|
|
const hours = Math.floor(timeDifference / (1000 * 60 * 60));
|
|
const minutes = Math.floor((timeDifference / (1000 * 60)) % 60);
|
|
const seconds = Math.floor((timeDifference / 1000) % 60);
|
|
dispatch({
|
|
type: 'SET_TIME_REMAINING',
|
|
payload: { hours, minutes, seconds }
|
|
});
|
|
}, 1000);
|
|
|
|
return () => clearInterval(intervalId);
|
|
}, [targetDate]);
|
|
|
|
return (
|
|
<span className={clsx('', className)}>
|
|
{`${timeRemaining.hours.toString().padStart(2, '0')}:${timeRemaining.minutes
|
|
.toString()
|
|
.padStart(2, '0')}:${timeRemaining.seconds.toString().padStart(2, '0')}`}
|
|
</span>
|
|
);
|
|
}
|