Было непросто найти работающее решение о том, как периодически запускать memo-safe функцию из функционального компонента.

Говоря о «безопасности мемо», я имею в виду случаи, когда целевая функция ссылается на состояние компонента (которое меняется во времени).

Я решил вообще пропустить запоминание, так как:

  1. Использование аргументов целевой функции в качестве предикатов очень затратно в JavaScript, если они (аргументы) не являются скалярами. Object экземпляры равенство всегда будет открытым вопросом в JavaScript, поэтому сравнение неизменяемых предикатов для правильного использования useMemo или useEffect в большинстве случаев может быть более сложной операцией, чем вызов нескольких дополнительных незапоминаемых обратных вызовов (если вы, конечно, не имеете дело с анимацией) .
  2. Я прошелся по кодовой базе своего текущего проекта и буквально не нашел ни одной целевой функции, которую нужно использовать в таком хуке и которая не ссылается на состояние вызывающего компонента.

Вот мой крючок:

const isInFocus = () => (
    document === undefined ||
    document.visibilityState === undefined
    ) ? true : document.visibilityState === "visible"
function useClock(
    func: Function,
    delay: number,
    skipUnfocused = false
): void {
    const funcRef = React.useRef<Maybe<Function>>()
    React.useEffect(() => { funcRef.current = func }, [func])
    
    React.useEffect(() => {
        const exec = () => {
            if (
                !funcRef.current ||
                (skipUnfocused && !isInFocus())
            ) return
            funcRef.current()
        }
        const i = setInterval(exec, delay)
        return () => clearInterval(i)
     }, [delay])
}