react hook 的 linter 喜欢对 DependencyList 严格要求。这会导致以下损坏的情况,其中 2 个事件处理程序相互依赖。由于函数是向addEventListener
注册的,我知道如果它们发生变化,它会导致内存泄漏,所以简单的事情就是清空依赖项列表——但是在玩 linter 规则时处理这个问题的正确方法是什么?
const onMouseMove = useCallback((e) => {
if (!isSwipe(e)) {
onMouseUp(e)
}
}, [onMouseUp])
const onMouseUp = useCallback((e) => {
document.removeEventListener('mousemove', onMouseMove)
}, [onMouseMove])
useCallback
本质上是useMemo
的一个版本,专门用于记忆函数。 如果两个函数是相互依赖的,不能和useCallback
分开记忆,可以和useMemo
一起记忆:
const { onMouseMove, onMouseUp } = useMemo(() => {
const onMouseMove = (e) => {
if (!isSwipe(e)) {
onMouseUp(e)
}
};
const onMouseUp = (e) => {
document.removeEventListener('mousemove', onMouseMove)
}
return { onMouseMove, onMouseUp };
}, [/* shared deps*/]);
这是我用来一起记忆函数的一般模式,我认为这是对一般问题的最简单的答案。 但是我不确定,查看此处的实际代码,这是最好的方法 - 如果onMouseMove
是与由于useMemo
重新计算而注册的函数不同的函数,则该removeEventListener
可能不起作用。 在实践中,可能更像是一种useEffect
用例。
经过一些研究,看起来(useCallback((在实践中经常失效(的解决方案也可以解决这个问题。一般的想法是记住一个指向指向最新函数的 ref 的函数。
这是一个肮脏的黑客,也可能在并发模式下导致问题,但就目前而言,这是Facebook的建议:如何从useCallback读取经常变化的值
。const useEventCallback = (fn) => {
const ref = useRef(fn)
useEffect(() => {
ref.current = fn
})
return useCallback((...args) => {
ref.current(...args)
}, [ref])
}
const onMouseMove = useEventCallback((e) => {
if (!isSwipe(e)) {
onMouseUp(e)
}
})
const onMouseUp = useEventCallback((e) => {
document.removeEventListener('mousemove', onMouseMove)
})