Search code examples
htmltypescriptuse-effectuse-stateuse-ref

Getting Proper Types for useRef with useEffect and Event Listeners


Here is the code in question, which closes a menu when the user clicks outside of it. I need to give proper types everywhere where I inserted "any":

//ANY
const ref = useRef<any>();

useEffect(() => {
  //ANY
  const checkIfClickedOutside = (event: any) => {
    // If the menu is open and the clicked target is not within the menu,
    // then close the menu
    if (open && ref.current && !ref.current.contains(event.target)) {
      setOpen(false);
    }
  };

  document.addEventListener("mousedown", checkIfClickedOutside);

  return () => {
    // Cleanup the event listener
    document.removeEventListener("mousedown", checkIfClickedOutside);
  };
}, [open]);

I have implemented ref in a div:

return (
<div className="wrapper" ref={ref}>

    <button className="nav-button" onClick={handleOpen}>{menu}</button>
    {open && children as React.ReactNode} 

</div>
)

The useEffect references this useState hook I created:

//State for Open and Closing Menu
const [open, setOpen] = useState<boolean>(false);

const handleOpen = (event: MouseEvent ) => {
  event.preventDefault();
  setOpen(!open);
};

Since useRef is implemented in a div, I tried the type HTMLDivElement, like so:

const ref = useRef<HTMLDivElement>();

But the prop in the div complained that:

  Types of property 'current' are incompatible.
  Type 'HTMLDivElement | undefined' is not assignable to type 'HTMLDivElement | null'.
  Type 'undefined' is not assignable to type 'HTMLDivElement | null'.

I then tried changing the type to HTMLDivElement | null, but then it complained about something else, and something else, and so on (this question is already long enough).

I've never implemented useRef before so I'm at a bit of a loss.

Any help is appreciated, thanks!


Solution

  • Seemed to have gotten it working.

    const ref = useRef<HTMLDivElement>(null);
    
    useEffect(() => {
        const checkIfClickedOutside = (event: globalThis.MouseEvent) => {
            if (open && ref.current && 
                !ref.current?.contains(event?.target as 
            HTMLDivElement)) {
            setOpen(false);
            }
          };
    
      document.addEventListener("mousedown", checkIfClickedOutside);
    
      return () => {
        document.removeEventListener("mousedown", checkIfClickedOutside);
      };
    }, [open]);