I need to create an event handler in React in a custom hook and I'd like the event handling function to be referentially equal across renders for ..reasons (yes I do see how I can do this w/o but I want to understand). Can I use useCallback as below to ensure the eventListener is referentially equal on every render by simply not mentioning variables it closes over in the dependencies (as below) or will that cause issues even if it's never passed to a child component so I don't need it to change value to trigger rerenders?
If not how can I achieve this? Does it matter if I can assume delay and fn don't change (except perhaps it's reference)?
export const useVisibleInterval = (fn, delay = 300000) => {
const interval_id = useRef(null)
const handleVisibilityChange = useCallback(() => {
if (document.hidden || interval_id.current) return
interval_id.current = setInterval(fn, delay)
fn()
}, [])
const clearInt = () => {
if (!interval_id.current) return
clearInterval(interval_id.current)
interval_id.current = null
}
const interval_run = () => {
if (document.hidden) {
document.addEventListener("visibilitychange", handleVisibilityChange)
clearInt()
} else fn()
}
const teardown = () => {
document.removeEventListener("visibilitychange", handleVisibilityChange)
clearInt()
}
useEffect(() => {
interval_id.current = setInterval(interval_run, delay)
return teardown
}, [])
}
At a really high level, I'd like to understand what the dependencies for useCallback do. Are they just to force reference inequality so child components that depend on it rerender? Or does React memoize the result of function execution so failing to include dependencies would give stale results?
Based on pilchard's comment I've figured out the answer (and that I was being an idiot).
The issue is merely about what values are updated when the function gets called with new arguments. References don't need to be listed in the dependencies of a useCallback so the way I can do this is simply to replace fn and delay with references that take on those values, e.g.,
ref_fn = useRef(fn)
useEffect(()=> {ref_fn.current = fn}, [fn]}
And likewise for delay (or stuff them into same ref). But, it turns out that I didn't even need the useCallback and referential equality and the code above reflects my deep confusion at the time and should be ignored.