DEV Community

Rafi
Rafi

Posted on Edited on

Implementing context menu using react hooks

Sometimes you want to override the browsers default context menu in your react app. You can easily do this with a simple custom react hook. Such custom hook should tell you the X and Y position of the context menu and boolean to say whether you should render the component.

Here is a simple implementation of such custom react hook

import { useEffect, useCallback, useState } from "react";

const useContextMenu = outerRef => {
  const [xPos, setXPos] = useState("0px");
  const [yPos, setYPos] = useState("0px");
  const [menu, showMenu] = useState(false);

  const handleContextMenu = useCallback(
    event => {
      event.preventDefault();
      if (outerRef && outerRef.current.contains(event.target)) {
        setXPos(`${event.pageX}px`);
        setYPos(`${event.pageY}px`);
        showMenu(true);
      } else {
        showMenu(false);
      }
    },
    [showMenu, outerRef, setXPos, setYPos]
  );

  const handleClick = useCallback(() => {
    showMenu(false);
  }, [showMenu]);

  useEffect(() => {
    document.addEventListener("click", handleClick);
    document.addEventListener("contextmenu", handleContextMenu);
    return () => {
      document.removeEventListener("click", handleClick);
      document.removeEventListener("contextmenu", handleContextMenu);
    };
  }, []);

  return { xPos, yPos, menu };
};

export default useContextMenu;
ðŸ”ī Hot News: rafi/implementing context menu using react hooks ke - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ”ī Hot News: rafi/implementing context menu using react hooks ke - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

The hook adds two event listener one to intercept the right click and other to intercept the click event.

  1. When you right click you can get X and Y position of the click using event.pageX and event.pageY
  2. When you left click you toggle the menu so that it gets hidden

Here is a Menu component that uses that hook

import React from "react";

import useContextMenu from "./useContextMenu";

const Menu = ({ outerRef }) => {
  const { xPos, yPos, menu } = useContextMenu(outerRef);

  if (menu) {
    return (
      <ul className="menu" style={{ top: yPos, left: xPos }}>
        <li>Item1</li>
        <li>Item2</li>
        <li>Item3</li>
      </ul>
    );
  }
  return <></>;
};

export default Menu;
ðŸ”ī Hot News: rafi/implementing context menu using react hooks ke - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ”ī Hot News: rafi/implementing context menu using react hooks ke - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

You render the Menu component based on the boolean and you pass the X and Y position as inline styles.

Here is the demo of the custom hook and here is corresponding source code.

Top comments (7)

ðŸ”ī Hot News: rafi/implementing context menu using react hooks ke - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™
 
alialamine profile image
Ali Al Amine •

How to implement this on table row? so user can right -click on a row and select an option.

ðŸ”ī Hot News: rafi/implementing context menu using react hooks ke - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™
 
rafi993 profile image
Rafi •

There are multiple ways to implement this depending on your table implementation. Are you expecting table rows to have constant height or variable height ?

ðŸ”ī Hot News: rafi/implementing context menu using react hooks ke - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™
 
alyalameen profile image
alyalameen •

Yes, rows will have fixed height, I appreciate your help

ðŸ”ī Hot News: rafi/implementing context menu using react hooks ke - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ Thread
 
rafi993 profile image
Rafi • • Edited

Here is a really simple implementation

ðŸ”ī Hot News: rafi/implementing context menu using react hooks ke - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ Thread
 
alialamine profile image
Ali Al Amine •

This is simple and awesome! Many thanks

ðŸ”ī Hot News: rafi/implementing context menu using react hooks ke - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™
 
oybekalimat profile image
oybekalimat •

Thanks for the hook.

Don't forget to add empty dependency to your useEffect, so it will behave like a ComponentDidMount. And also there is typo in removing click listener.

ðŸ”ī Hot News: rafi/implementing context menu using react hooks ke - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™
 
timojkankaanpaa profile image
Timo KankaanpÃĪÃĪ • • Edited

Hi! Tried to apply your idea to mui-TreeView to create context menu there. Could you help me with some issue there?