Search code examples
konvajsreact-konva

Hovering over icons on top of an element with onMouseOver in react-konva


I have a <Group/> element containing a rectangle and a set of icons on top of the group that should only be visible if the user moves the cursor within the boundaries of that <Group/>'s contents. Wether the icons are being shown or not is triggered by an onMouseOver and onMouseOut event handler attached to the <Group/> object. I use a useState hook to save the hover state and hide/show the icons.

Displaying the icons onMouseOver works fine. But whenever the mouse cursor hovers over an icon, the onMouseOut event of the <Group/> is triggered, thus hiding the icons. Moving the mouse over an icon shows/hides them in rapid succession (flashing).

Is there a way to force the <Group/> to not trigger an onMouseOut event if the cursor is still actually within it's area but on top of an icon (event bubbling?)?


Solution

  • It is possible to workaround this by using React.useRef() to store the hover state of the elements when they change and use the reference within a delayed function to decide what to do. In essence, something like this:

    Setting up state, references and updating refs:

    ...
    
        const [isGroupHover, setIsGroupHover] = useState(false);
        const [isContextMenuHover, setIsContextMenuHover] = useState(false);
    
        const isContextMenuHovereRef = useRef(isContextMenuHover);
        const isGroupHoverRef = useRef(isGroupHover);
    
        useEffect(() => {
          isContextMenuHovereRef.current = isContextMenuHover;
          isGroupHoverRef.current = isGroupHover;
        }, [isGroupHover, isContextMenuHover]);
    
    ...
    

    Intercepting the <Group/>'s onMouseOut event

    ...
    
        const onMouseOut = (e): void => {
          setTimeout(() => {
            if (!isContextMenuHovereRef.current) {
              setIsGroupHover(false);
            }
          }, 350);
        };
    
    ...
    

    A function (setIsContextMenuHover()) is passed down to the context menu component via prop. This way, the context menu can pass it's own hover-state up the chain.

    ...
    
    <ContextMenu setOnHover={setIsContextMenuHover} />
    
    ...
    

    Essentially, this prevents the hover state of the <Group/>from being changed if the context menu is still visible or hovered over. A bit unwieldy - but it works.