From a752479f6c7e31148a4f8340173535558ff79228 Mon Sep 17 00:00:00 2001 From: Zyc Date: Wed, 20 Mar 2024 17:52:10 +1100 Subject: [PATCH 1/2] repair edge-case breaking useHover --- packages/react-laag/src/useHover.ts | 61 +++++++++++++++-------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/packages/react-laag/src/useHover.ts b/packages/react-laag/src/useHover.ts index a1404d0..dc74f8b 100644 --- a/packages/react-laag/src/useHover.ts +++ b/packages/react-laag/src/useHover.ts @@ -1,3 +1,4 @@ +// fixing buggy version of https://github.com/everweij/react-laag/blob/master/packages/react-laag/src/useHover.ts import { useState, useRef, useCallback, useEffect, MouseEvent } from "react"; export type UseHoverOptions = { @@ -28,12 +29,6 @@ export type UseHoverProps = { onTouchEnd: PlainCallback; }; -enum Status { - ENTERING, - LEAVING, - IDLE -} - export function useHover({ delayEnter = 0, delayLeave = 0, @@ -41,21 +36,25 @@ export function useHover({ }: UseHoverOptions = {}): readonly [boolean, UseHoverProps, () => void] { const [show, setShow] = useState(false); - const timeout = useRef(null); - - const status = useRef(Status.IDLE); + // single state: + // when enterTimeout is set, then it's also entering + const enterTimeout = useRef(null); + // when exitTimeout is set, then it's also leaving + const exitTimeout = useRef(null); const hasTouchMoved = useRef(false); + // cleanup all timeouts const removeTimeout = useCallback(function removeTimeout() { - clearTimeout(timeout.current!); - timeout.current = null; - status.current = Status.IDLE; + if(enterTimeout.current) clearTimeout(enterTimeout.current); + if(exitTimeout.current) clearTimeout(exitTimeout.current); + enterTimeout.current = null; + exitTimeout.current = null; }, []); function onMouseEnter() { // if was leaving, stop leaving - if (status.current === Status.LEAVING && timeout.current) { + if (exitTimeout.current) { removeTimeout(); } @@ -63,18 +62,20 @@ export function useHover({ return; } - status.current = Status.ENTERING; - timeout.current = window.setTimeout(() => { + // we're already entering and this is just a second onMouseEnter event, e.g., from a child element or maybe we have two triggers + if(enterTimeout.current) return; + + // schedule entering + enterTimeout.current = window.setTimeout(() => { setShow(true); - timeout.current = null; - status.current = Status.IDLE; + enterTimeout.current = null; }, delayEnter); } - function onMouseLeave(_: MouseEvent, immediate?: boolean) { + function onMouseLeave(_: MouseEvent, immediate?: boolean) { // if was waiting for entering, // clear timeout - if (status.current === Status.ENTERING && timeout.current) { + if (enterTimeout.current) { removeTimeout(); } @@ -84,16 +85,17 @@ export function useHover({ if (immediate) { setShow(false); - timeout.current = null; - status.current = Status.IDLE; + removeTimeout(); return; } - status.current = Status.LEAVING; - timeout.current = window.setTimeout(() => { + // we're already leaving and this is just a second onMouseEnter event + if(exitTimeout.current) return; + + // schedule leaving + exitTimeout.current = window.setTimeout(() => { setShow(false); - timeout.current = null; - status.current = Status.IDLE; + exitTimeout.current = null; }, delayLeave); } @@ -110,12 +112,13 @@ export function useHover({ return () => { window.removeEventListener("scroll", onScroll, true); - - if (timeout.current) { - clearTimeout(timeout.current); - } }; }, [show, hideOnScroll, removeTimeout]); + useEffect(() => { + return () => { + removeTimeout() + }; + }, [removeTimeout]) const hoverProps: UseHoverProps = { onMouseEnter, From 895f104198e48e2f1bcdb607fa5d17fa60235b6b Mon Sep 17 00:00:00 2001 From: Zyc Date: Wed, 20 Mar 2024 17:53:13 +1100 Subject: [PATCH 2/2] remove comment --- packages/react-laag/src/useHover.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-laag/src/useHover.ts b/packages/react-laag/src/useHover.ts index dc74f8b..5c1e26f 100644 --- a/packages/react-laag/src/useHover.ts +++ b/packages/react-laag/src/useHover.ts @@ -1,4 +1,3 @@ -// fixing buggy version of https://github.com/everweij/react-laag/blob/master/packages/react-laag/src/useHover.ts import { useState, useRef, useCallback, useEffect, MouseEvent } from "react"; export type UseHoverOptions = {