The code is for a Navbar
. I want this when the mouse got out of it this timer started, but while this time was counting if I moved my mouse over the Navbar
, the timer counting being stopped and changed displayed to none dont being applied. The problem is when I move the mouse out of the Navbar
and then over it again the timer does not stop and by the end of the counting dropdown display is gone.
here is my code:
const [navOver, setNavOver] = useState(false);
const [currentItem, setCurrentItem] = useState("");
let timer;
const handleMouseOver = (item) => {
setNavOver(true);
clearTimeout(timer);
for (var i = 0; i < dropdown.length; i += 1) {
if (dropdown[i].style.display === "none") {
dropdown[i].style.display = "block";
}
}
if (!currentItem) {
setCurrentItem(item);
}
};
const handleMouseOut = (event) => {
if (!event.relatedTarget || !event.relatedTarget.closest("nav")) {
setNavOver(false);
timer = setTimeout(() => {
for (var i = 0; i < dropdown.length; i += 1) {
dropdown[i].style.display = "none";
}
}, 700);
}
};
and jsx code:
<nav onMouseOut={handleMouseOut}>
<ul className="lg:flex hidden gap-4 ps-4">
{navLinks.map((item, index) => {
return (
<>
<li key={index} className="z-50">
<Link
to={item.href}
onMouseOver={() => {
handleMouseOver(item);
setCurrentItem(item);
}}
>
{item.label}
</Link>
</li>
<div className="dropdownContainer">
// there was dropdown component
</div>
</>
);
})}
</ul>
</nav>
I wanna stop the timer in handleMouseOut
using the handleMouseOver
function.
Since let timer;
is declared in the body of the component, it's being re-defined every render. So by the time handleMouseOver
is trying to run clearTimeout(timer);
timer is actually undefined. This is a case where a ref could be used to hold the value/referance of timer
between renders.
so doing this should get what you want.
const timerRef=useRef(null);
const handleMouseOver = (item) => {
setNavOver(true);
if(timerRef.current) clearTimeout(timerRef.current);
for (var i = 0; i < dropdown.length; i += 1) {
if (dropdown[i].style.display === "none") {
dropdown[i].style.display = "block";
}
}
if (!currentItem) {
setCurrentItem(item);
}
};
const handleMouseOut = (event) => {
if (!event.relatedTarget || !event.relatedTarget.closest("nav")) {
setNavOver(false);
timerRef.current = setTimeout(() => {
for (var i = 0; i < dropdown.length; i += 1) {
dropdown[i].style.display = "none";
}
}, 700);
}
};
You're also potentially creating a memory leak, so you should also clean up the timer if the page un-mounts, by cleaning it up in the return of a useEffect like so.
useEffect(()=>{
return()=> {
if(timerRef.current) clearTimeout(timerRef.current);
}
},[])