sllmn
sllmn

Reputation: 151

What is the best way to replace id in react?

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

Answers (1)

DSDmark
DSDmark

Reputation: 1271

Hi @sllmn,

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

Related Questions