import React, { useEffect, useRef } from 'react'
function useOnClickOutside<T extends HTMLElement>(
onClick: (event: MouseEvent) => void,
deps?: React.DependencyList
): React.RefObject<T> {
const nodeRef = useRef<T>(null)
const handleClickAnywhere = (event: MouseEvent) => {
// 如果点击的元素不在目标元素内,处理回调 onClick
if (nodeRef.current && !nodeRef.current.contains(event.target as Node)) {
onClick(event)
}
}
// 监听文档中的任何点击事件
useEffect(() => {
document.addEventListener('mousedown', handleClickAnywhere)
return () => document.removeEventListener('mousedown', handleClickAnywhere)
}, deps || [])
return nodeRef
}
export default useOnClickOutside;
🤔 为什么需要传 deps?
为了避免闭包陷阱。useOnClickOutside 的 onClick 内如果有用到其他变量,而 useEffect 的回调函数形成了闭包,变量总是初始值(比如下面例子中 contentCompleted 总是 false)。
使用:
const Demo: React.FC<{ contentCompleted: boolean }> = ({ contentCompleted }) => {
const containerRef = useOnClickOutside<HTMLDivElement>(() => {
if (contentCompleted) {
onClose()
}
}, [contentCompleted])
return <div ref={containerRef}>...</div>
}