Reputation: 151
I am working on a menu component. And with the help of the id, I made it possible for it to open and close on a click outside the component.
But I understand that I'm doing something wrong. If I open other menus, then the previous one does not close, I tried to pass props to override the id, but they are unstable (in fact, they stopped working altogether, but I didn’t dive further and look for an answer why)
I understand that this is not best practice. The component must be able to be reused. So I think how to improve it, should I use refs? Hook onChange? Which method do you think is more convenient, more practical?
import { useState } from "react";
function OverflowMenu(props) {
const [isActive, setIsActive] = useState(false);
const handleClick = (event) => {
setIsActive((current) => !current);
document.addEventListener("click", (event) => {
const target = event.target;
if (!target.closest(`#wrapper`) && !target.closest(`#menu`)) {
setIsActive(false);
}
});
};
return (
<>
<div
id={"wrapper"}
onClick={handleClick}
className="relative flex cursor-pointer"
>
{props.children} {props.content}
<div
id={"menu"}
className={`${!isActive && "hidden"}
${props.topRight && "-right-0 bottom-full"}
${props.topLeft && "-left-0 bottom-full"}
${props.bottomRight && "-right-0 top-full"}
${props.bottomLeft && "-left-0 top-full"}
absolute z-20 my-[4px] flex min-w-[112px] max-w-[280px] flex-col`}
>
<div className="scroll flex max-h-[310px] min-w-max flex-col overflow-hidden overflow-y-auto rounded-[8px] bg-blue-50 py-[8px] shadow-mm-1 dark:bg-gray-800 ">
<>{props.menu}</>
</div>
</div>
</div>
</>
);
}
export { OverflowMenu };
Upvotes: 1
Views: 320
Reputation: 1271
There are a few ways you could improve the current implementation.
One option would be to use a ref
hook. You could use that for passing the reference of HTML
element.
const menuRef = useRef(null)
const handleClick = (event) => {
setIsActive((current) => !current)
if (!menuRef.current.contains(event.target)) {
setIsActive(false)
}
}
// code....
<div
id={'menu'}
ref={menuRef}
className={""}>
// code....
</div>
Another option. Which I found on internet, and it's a good one. To use useOnClickOutside
hook. You will need to install it first.
yarn add react-use
Now you can use by importing that library.
import { useOnClickOutside } from 'react-use';
import { useState, useRef } from 'react';
import { useOnClickOutside } from 'react-use';
function OverflowMenu(props) {
const [isActive, setIsActive] = useState(false);
const menuRef = useRef(null);
useOnClickOutside(menuRef, () => setIsActive(false));
const handleClick = (event) => {
setIsActive((current) => !current);
};
return (
<>
<div
id={"wrapper"}
onClick={handleClick}
className="relative flex cursor-pointer"
>
{props.children} {props.content}
<div
id={"menu"}
ref={menuRef}
className={`${!isActive && "hidden"}
${props.topRight && "-right-0 bottom-full"}
${props.topLeft && "-left-0 bottom-full"}
${props.bottomRight && "-right-0 top-full"}
${props.bottomLeft && "-left-0 top-full"}
absolute z-20 my-[4px] flex min-w-[112px] max-w-[280px] flex-col`}
>
<div className="scroll flex max-h-[310px] min-w-max flex-col overflow-hidden overflow-y-auto rounded-[8px] bg-blue-50 py-[8px] shadow-mm-1 dark:bg-gray-800 ">
<>{props.menu}</>
</div>
</div>
</div>
</>
);
}
export { OverflowMenu };
Upvotes: 1