File size: 1,673 Bytes
676fc08
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import { useState, useMemo, useRef, useEffect } from "react";

function useAccurateTimer() {
  const [time, setTime] = useState(0);
  const [isRunning, setIsRunning] = useState(false);
  const timerRef = useRef<number | null>(null);
  const startTimeRef = useRef<number | null>(null);

  // Format time, recalculate only when time changes
  const formattedTime = useMemo(() => {
    const seconds = Math.floor(time / 1000);
    const milliseconds = time % 1000;
    return `${seconds}.${Math.floor(milliseconds / 100)
      .toString()
      .padStart(1, "0")}s`;
  }, [time]);

  // Use useCallback to optimize start and stop functions to avoid unnecessary re-rendering
  function start() {
    setTime(0);
    if (!timerRef.current) {
      setIsRunning(true);
      // Record the start time, taking into account the pause situation
      startTimeRef.current = Date.now() - time;

      const tick = () => {
        if (startTimeRef.current !== null) {
          setTime(Date.now() - startTimeRef.current);
          // Using requestAnimationFrame
          timerRef.current = requestAnimationFrame(tick);
        }
      };

      // Start the animation frame loop
      timerRef.current = requestAnimationFrame(tick);
    }
  }

  function stop() {
    if (timerRef.current) {
      setIsRunning(false);
      cancelAnimationFrame(timerRef.current);
      timerRef.current = null;
    }
    setTime(0);
  }

  useEffect(() => {
    return () => {
      if (timerRef.current) {
        cancelAnimationFrame(timerRef.current);
      }
    };
  }, []);

  return {
    time,
    formattedTime,
    isRunning,
    start,
    stop,
  };
}

export default useAccurateTimer;