/ src / hooks / useMinDisplayTime.ts
useMinDisplayTime.ts
 1  import { useEffect, useRef, useState } from 'react'
 2  
 3  /**
 4   * Throttles a value so each distinct value stays visible for at least `minMs`.
 5   * Prevents fast-cycling progress text from flickering past before it's readable.
 6   *
 7   * Unlike debounce (wait for quiet) or throttle (limit rate), this guarantees
 8   * each value gets its minimum screen time before being replaced.
 9   */
10  export function useMinDisplayTime<T>(value: T, minMs: number): T {
11    const [displayed, setDisplayed] = useState(value)
12    const lastShownAtRef = useRef(0)
13  
14    useEffect(() => {
15      const elapsed = Date.now() - lastShownAtRef.current
16      if (elapsed >= minMs) {
17        lastShownAtRef.current = Date.now()
18        setDisplayed(value)
19        return
20      }
21      const timer = setTimeout(
22        (shownAtRef, setFn, v) => {
23          shownAtRef.current = Date.now()
24          setFn(v)
25        },
26        minMs - elapsed,
27        lastShownAtRef,
28        setDisplayed,
29        value,
30      )
31      return () => clearTimeout(timer)
32    }, [value, minMs])
33  
34    return displayed
35  }