Triviaverse / src /hooks /useTimer.ts
Simeon Garratt
feat: remove Supabase, bundle questions locally, use JSON DB + API
ad111f4
Raw
History Blame Contribute Delete
1.7 kB
"use client";
import { useState, useRef, useCallback, useEffect } from "react";
interface UseTimerOptions {
initialSeconds: number;
onTimeUp: () => void;
autoStart?: boolean;
}
export function useTimer({ initialSeconds, onTimeUp, autoStart = false }: UseTimerOptions) {
const [seconds, setSeconds] = useState(initialSeconds);
const [isRunning, setIsRunning] = useState(autoStart);
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
const onTimeUpRef = useRef(onTimeUp);
useEffect(() => {
onTimeUpRef.current = onTimeUp;
}, [onTimeUp]);
const stop = useCallback(() => {
setIsRunning(false);
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
}, []);
const start = useCallback(() => {
setIsRunning(true);
}, []);
const reset = useCallback((newSeconds?: number) => {
stop();
setSeconds(newSeconds ?? initialSeconds);
}, [stop, initialSeconds]);
// Tick the timer — pure updater, no side effects
useEffect(() => {
if (!isRunning) return;
intervalRef.current = setInterval(() => {
setSeconds((prev) => {
if (prev <= 1) return 0;
return prev - 1;
});
}, 1000);
return () => {
if (intervalRef.current) clearInterval(intervalRef.current);
};
}, [isRunning, stop]);
// Fire side effects when timer reaches 0
useEffect(() => {
if (isRunning && seconds === 0) {
const timer = setTimeout(() => {
stop();
onTimeUpRef.current();
}, 0);
return () => clearTimeout(timer);
}
}, [seconds, isRunning, stop]);
return { seconds, isRunning, start, stop, reset };
}