I have a component with another component inside it that I only want to show if the user is still hovering after a short delay.
So I wrote a couple handlers for onMouseEnter
and onMouseExit
to set a variable to show that its hovered. Then, after sleeping, if it's still hovered I want to set a second variable to show the element.
However, hovered
is showing as false
no matter what. Why?
const [hovered, setHovered] = useState(false);
const [show, setShow] = useState(false);
console.log('hovered', hovered); // This shows the state correctly
const handleEnter = () => {
setHovered(true);
sleep(2000).then(() => {
console.log('checking', hovered); // This always shows false
if (hovered) {
setShow(true);
}
});
}
const handleExit = () => {
setHovered(false);
setShow(false);
}
Edit
Solution:
Replace sleep with a wrapped callback from use-debounce
to prevent it from firing multiple times, and have the delay work still.
const ref = useRef(false);
const [hovered, setHovered] = useState(false);
const [show, setShow] = useState(false);
const handleHover = useDebouncedCallback(() => {
if (ref.current) setShow(true);
else setShow(false);
}, 1000);
useEffect(() => {
ref.current = hovered;
}, [hovered]);
useEffect(() => {
handleHover()
}, [hovered]);
CodePudding user response:
i would recommend you to use useRef hook.
const hoveredRef = useRef(false);
const [hovered, setHovered] = useState(false);
const [show, setShow] = useState(false);
useEffect(() => {
hoveredRef.current = hovered;
}, [hovered])
useEffect(() => {
if (!hovered) return;
sleep(2000).then(() => {
console.log('checking', hoveredRef.current);
if (hoveredRef.current) {
setShow(true);
}
}, [hovered])
const handleEnter = () => {
setHovered(true);
}
const handleExit = () => {
setHovered(false);
setShow(false);
}
I didnt check it but should be ok, i dont have the rest of your code, sorry
Regarding your question Why:
const handleEnter = () => {
setHovered(true);
sleep(2000).then(() => {
console.log('checking', hovered); // This always shows false
if (hovered) {
setShow(true);
}
});
}
SetHover that you are calling here does not change the variable immediately, for all the code after this line in function it will still be false
. And in the sleep.then function scope it still uses a closure-captured old "false" value.
And a little warning here, code above is not ideal, if you hover in-out 5 times within your 2 second delay - code inside sleep.then will fire 5 times successfully. But it is harmless in your case, in terms of behavior.