"use client" import { RefObject, useState, useEffect } from "react" export const useHover = ( ...refs: RefObject[] ): boolean => { const [isHovered, setIsHovered] = useState(false) useEffect(() => { let mouseEnteredTimeout: number | undefined = undefined const handleMouseEnter = () => { setIsHovered(true) // If the user enters the hoverable and then quickly leaves, it's possible for the mouseleave event to not fire as // the initial mouse enter event hasn't finished registering. Check if any of the refs have `:hover` after a delay // if not, then we can assume the mouse has left the hoverable element mouseEnteredTimeout = window.setTimeout(() => { if (!refs.some(ref => ref.current?.matches(":hover"))) { setIsHovered(false) } }, 200) } // This makes it such that if the mouse leaves on one hoverable element to another, it doesn't set isHovered to false const handleMouseLeave = (e: MouseEvent) => { if (refs.find(ref => ref.current === e.relatedTarget)) { return } setIsHovered(false) } refs.forEach(ref => { const node = ref.current if (node) { node.addEventListener("mouseenter", handleMouseEnter) node.addEventListener("mouseleave", handleMouseLeave) } }) return () => { refs.forEach(ref => { const node = ref.current if (node) { node.removeEventListener("mouseenter", handleMouseEnter) node.removeEventListener("mouseleave", handleMouseLeave) } }) if (mouseEnteredTimeout) { clearTimeout(mouseEnteredTimeout) } } }, [refs]) return isHovered }