skip to Main Content

I have an Overlay which is opened by clicking an icon button. In that Overlay, I’m using the useClickAway hook exported by the react-use library –

useClickAway(overlayRef, hideOverlay);

Basically, whenever a user clicks anywhere outside the overlay dom node, it hides this overlay.

But with this, I’m facing one issue when a user clicks on that icon button (which opens this overlay), this overlay first closes and then re-opens, which causes the flickering of the overlay.

How can I make clicking on this icon button an exception for this useClickAway hook?

I have tried adding event.stopPropagation to click event handler on icon button but it didn’t work.

Codesandbox Link for same – https://codesandbox.io/s/useclickaway-hook-krd6y6

3

Answers


  1. Chosen as BEST ANSWER

    Have fixed it by adding following event listener to button -

    onMouseDown={(event) => {
       event.stopPropagation();
    }}
    

  2. It sounds like you want to prevent the overlay from immediately closing and reopening when the icon button is clicked, while still allowing it to close when clicking outside the overlay. Since the useClickAway hook triggers the hideOverlay function when clicking outside the overlay, you’ll need a mechanism to temporarily disable this behavior when the icon button is clicked.

    One way to achieve this is by using a flag to determine whether the overlay should be closed or not based on the last click event target. Here’s an example of how you could modify your code to achieve this:

    import React, { useState, useRef } from 'react';
    import { useClickAway } from 'react-use';
    
    const Overlay = () => {
      const overlayRef = useRef(null);
      const [isOpen, setIsOpen] = useState(false);
      const [shouldClose, setShouldClose] = useState(true); // Add a flag
    
      const toggleOverlay = () => {
        setIsOpen(!isOpen);
      };
    
      const hideOverlay = () => {
        if (shouldClose) {
          setIsOpen(false);
        } else {
          setShouldClose(true);
        }
      };
    
      useClickAway(overlayRef, hideOverlay);
    
      return (
        <div>
          <button onClick={toggleOverlay}>Toggle Overlay</button>
          {isOpen && (
            <div ref={overlayRef}>
              {/* Overlay content */}
              <button
                onClick={(e) => {
                  setShouldClose(false); // Prevent closing
                  e.stopPropagation();
                }}
              >
                Icon Button
              </button>
              {/* Other overlay content */}
            </div>
          )}
        </div>
      );
    };
    
    export default Overlay;
    

    In this example, the shouldClose flag is used to determine whether the overlay should be closed when the useClickAway hook triggers the hideOverlay function. When the icon button is clicked, the shouldClose flag is temporarily set to false, preventing the overlay from closing immediately. The e.stopPropagation() call prevents the event from propagating and affecting the overlay’s closing behavior.

    Remember that the above example assumes a basic overlay structure. You might need to adapt it to fit your specific code and styling.

    Login or Signup to reply.
  3. The onClickAway hook doesn’t support excluding HTML elements, its callback will be called if the element you click is not the descendant element of the overlay element. See !el.contains(event.target).

    You have to exclude the HTML elements by yourself. You can get the mousedown event object in the handler. See onClickAway: (event: E) => void

    If the event.target is the button element, don’t close the overlay.

    import "./styles.css";
    import { useState, useRef } from "react";
    import useClickAway from "react-use/lib/useClickAway";
    
    export default function App() {
      const [isOpen, setIsOpen] = useState(false);
      const overlayRef = useRef<null | HTMLDivElement>(null);
      const buttonRef = useRef<HTMLButtonElement | null>(null);
    
      useClickAway(overlayRef, (event) => {
        if(event?.target === buttonRef.current) return;
        setIsOpen(false);
      });
    
      return (
        <div className="App">
          <button
            ref={buttonRef}
            onClick={() => {
              setIsOpen(true);
            }}
          >
            Open Overlay
          </button>
          {isOpen ? (
            <div ref={overlayRef} id="overlay">
              Overlay
            </div>
          ) : null}
        </div>
      );
    }
    

    codesandbox

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search