创建 React 上下文菜单

如果在访问网站时右键单击浏览器,则可以看到操作系统的本机上下文菜单。从那里,您可以保存、打印、为页面创建二维码等等。如果突出显示文本,则可以看到复制、粘贴和剪切等选项。您还可以在电子邮件或列表应用程序以及协作应用程序(如 Trello 和 Nottion)上看到自定义的上下文菜单。这些右键单击菜单(也称为上下文菜单)在用户使用应用时为用户提供了更多选项。

这篇文章将探讨如何创建一个 React 上下文菜单,激活右键菜单的快捷方式,以及如何创建自定义上下文菜单 Hook。

您可以在下面查看项目演示,并在 GitHub 或已部署的网站上查看完整的代码。

什么是 React 上下文菜单?

上下文菜单(也称为右键单击菜单)是在用户交互(如右键单击鼠标操作)上显示的图形用户界面 (GUI) 菜单。如何在Android上录制WhatsApp视频和语音通话?上下文菜单提供一组有限的选项,这些选项在菜单所属的操作系统或应用程序的当前状态或上下文中可用。

开始使用我们的 React 项目

要开始在 React 中创建自定义右键单击菜单,我们将使用该命令初始化一个 React 项目,然后使用命令进入项目文件夹。在本教程中,我们将使用样式化组件进行样式设置:npx create-react-app react-context-menu``cd react-context-menu

项目和文件夹结构

在我们的文件中,我们将创建四个文件夹:src

  • components:这将容纳我们将在整个项目中使用的所有组件

  • data:这将存储将在网页上呈现的数据信息

  • hooks:这是我们将为右键单击上下文菜单创建钩子的地方

  • styles:此文件夹将包含我们所有样式的文件

如何禁用右键单击上下文菜单

如果您转到Chrome或Mozilla浏览器并右键单击任意位置,您将看到默认的浏览器上下文菜单,如下所示:

我们将使用 prop 在我们的文件中禁用如何在 Windows 11 上解压缩 .tar.gz、.tgz、.gz 文件?分享6种方法此默认行为。它将看起来像这样:onContextMenu``App.js

src/App.js
​
 import "./App.css";
function App() {
  return (
    <div
      className="App"
      onContextMenu={(e) => {
        e.preventDefault(); // prevent the default behaviour when right clicked
        console.log("Right Click");
      }}
    >
      Context Menu
      </div>
  );
}
export default App;

现在,让我们看看浏览器中有什么:

如您所见,右键单击浏览器的任何其他部分时,将显示默认菜单,但是右键单击上下文时,它不会显示,并且控制台中会记录文本。

在 React 中创建自定义的右键单击上下文菜单

首先,我们将在文件夹中创建一个文件,并使用我们将用于项目的数据填充该文件:data.js``data

src/data.js
​
export const data = [
  {
    id: 1,
    title: "Message 1",
  },
  {
    id: 2,
    title: "Message 2",
  },
  {
    id: 3,
    title: "Message 3",
  },
  {
    id: 4,
    title: "Message 4",
  },
];

然后,我们将在名为的文件夹中创建一个新组件,并在我们的 :componentMenuContext.jsApp.js

src/App.js
​
import "./App.css";
import MenuContext from "./components/MenuContext";
import { data } from "./data/data";
function App() {
  return (
    <div className="App">
      <MenuContext  />
    </div>
  );
}
export default App;
​
src/component/MenuContext.js
​
import React, { useState, useEffect } from "react";
const MenuContext = () => {
  return (
    <div>
      MenuContext
    </div>
  );
};
export default MenuContext;

现在,我们的浏览器中应该有这样的东西:

在上一节中,我们提到我们需要禁用浏览器的默认右键单击上下文菜单。我们将使用道具,如前所述:onContextMenu

src/component/MenuContext.js
​
    <div
      onContextMenu={(e) => {
        e.preventDefault();
        console.log("Right Click");
      }}
    >
      MenuContext
    </div>

在这里,我们使用 来防止默认的浏览器上下文菜单行为,并采取 来防止浏览器的默认行为。当我们在浏览器中右键单击时,指定了控制台日志以在控制台中显示文本,因为我们无法再次看到浏览器的默认上下文菜单:onContextMenu``event method

右键单击并阻止默认浏览器行为后,控制台 () 中记录了一条文本,以显示我们右键单击。Right Click

实现 React 自定义右键菜单

现在默认的右键单击自定义菜单已被禁用,我趣知笔记们可以继续自定义右键单击菜单实现。

首先,我们将数据导入组件并将其作为道具传递给组件。在 中,我们将解构这个 prop () 并映射到它以将数据呈现到浏览器 UI:App.jsMenuContextMenuContext``data

src/App.js
​
import "./App.css";
import MenuContext from "./components/MenuContext";
import { data } from "./data/data";
function App() {
  return (
    <div className="App">
      <MenuContext data={data} />
    </div>
  );
}
export default App;
​
src/components/MenuContext.js
​
import React, { useState, useEffect } from "react";
import Menu from "./Menu";
const MenuContext = ({ data }) => {
   return (
    <div>
      {data.map((item) => (
        <div
          onContextMenu={(e) => {
            e.preventDefault();
            console.log("Right Click", e.pageX, e.pageY);
          }}
        >
          <Menu key={item.id} title={item.title} />
        </div>
      ))}
    </div>
  );
};
export default MenuContext;

在这里,我们在数据数组上映射,返回一个组件,并传入 和 .在文件中,我们将按如下方式使用这两个道具:MenuIDtitle``Menu.js

src/components/Menu.js
​
import React from "react";
import { MenuContextContainer } from "../styles/styles";
const Menu = ({ title, key }) => {
  return (
    <>
      <MenuContextContainer key={key}>{title}</MenuContextContainer>
    </>
  );
};
export default Menu;

是来自该文件的样式化组件:MenuContextContainer``styles

src/styles/styles.js
​
import styled, { css } from "styled-components";
export const MenuContextContainer = styled.div`
  border: 1px solid #ffffff2d;
  border-radius: 4px;
  padding: 18px;
  margin: 5px 0;
  box-sizing: border-box;
`;

现在,我们可以检查浏览器中的内容:

显示右键单击上下文菜单

当我们单击任何消息框时,将记录控制台文本。现在,我们需要实现它,以便在右键单击任何框时右键单击上下文菜单显示一次,而不是四次。趣知笔记网站地图这是因为我们在 UI 上渲染了四个项目,并且菜单显示在鼠标指向的任何位置:"Right Click"

src/compoennts/MenuContext.js
​
import React, { useState, useEffect } from "react";
import { ContextMenu } from "../styles/styles";
import Menu from "./Menu";
const MenuContext = ({ data }) => {
  const [clicked, setClicked] = useState(false);
  const [points, setPoints] = useState({
    x: 0,
    y: 0,
  });
  useEffect(() => {
    const handleClick = () => setClicked(false);
    window.addEventListener("click", handleClick);
    return () => {
      window.removeEventListener("click", handleClick);
    };
  }, []);
  return (
    <div>
      {data.map((item) => (
        <div
          onContextMenu={(e) => {
            e.preventDefault();
            setClicked(true);
            setPoints({
              x: e.pageX,
              y: e.pageY,
            });
            console.log("Right Click", e.pageX, e.pageY);
          }}
        >
          <Menu id={item.id} title={item.title} />
        </div>
      ))}
      {clicked && (
        <ContextMenu top={points.y} left={points.x}>
          <ul>
            <li>Edit</li>
            <li>Copy</li>
            <li>Delete</li>
          </ul>
        </ContextMenu>
      )}
    </div>
  );
};
export default MenuContext;

在文件中,我们创建两个状态:和 .状态(具有布尔值)将在右键单击鼠标时进行监视。(具有对象状态)将用于获取单击鼠标的位置的 和坐标。MenuContext.jsclickedpointsclickedpointsxy

使用钩子useEffect

和 的值将默认设置为 。然后,我们必须这样做,以便当我们单击页面上的任意位置时,上下文菜单会消失,就像它适用于默认的自定义右键单击菜单文本一样。为了实现这一点,我们将通过调用 useEffect Hook 并创建一个名为 .我们还会将状态设置为不显示它。xy0handleClickclicked``false

Then, we will reference the window, call the method, and register a event so that whenever we click anywhere in the window or browser, the document will call . Lastly, we will attach a cleanup function to remove the event listener to avoid memory leaks.addEventListenerclickhandle click

The event object contains the values of the present coordinates being clicked. To make it clearer, we can log and in the prop:e.xe.yonContextMenu

src/MenuContext.js

  <div>
      {data.map((item) => (
        <div
          onContextMenu={(e) => {
            e.preventDefault();
            setClicked(true);
            setPoints({
              x: e.pageX,
              y: e.pageY,
            });
            console.log("Right Click", e.pageX, e.pageY);
          }}
        >
          <Menu id={item.id} title={item.title} />
        </div>
      ))}
      {clicked && (
        <ContextMenu top={points.y} left={points.x}>
          <ul>
            <li>Edit</li>
            <li>Copy</li>
            <li>Delete</li>
          </ul>
        </ContextMenu>
      )}
    </div>

现在,我们可以检查控制台以查看单击浏览器上的不同位置时记录的内容:

我单击了渲染项目的四个位置,并记录或返回了 和坐标值。x``y

在包装和我们的项目(我们已经启动道具的地方)中,我们将状态设置为 和 和 单击的坐标。divMenuonCotextMenuclickedtruepointsx``y

设置 React 上下文菜单的样式

ContextMenu`是文件中样式化的 div。仅当单击为 true 时,才会显示它。例如,仅当单击任何**消息框**时,因为我们将其设置为包装元素。`styles.js``click``true``div``message boxes

请记住,我们状态的默认值是 。和 的值作为 props 传递给 div,并用于设置应显示上下文菜单的位置的样式。最后,在 中创建了一个无序列表,当右键单击任何项目 () 时将显示该列表:clickedfalsexyContextMenuContextMenuMessage 1- 4

src/styles/styles.js

import styled, { css } from "styled-components";
export const MenuContextContainer = styled.div`
  border: 1px solid #ffffff2d;
  border-radius: 4px;
  padding: 18px;
  margin: 5px 0;
  box-sizing: border-box;
`;
export const ContextMenu = styled.div`
  position: absolute;
  width: 200px;
  background-color: #383838;
  border-radius: 5px;
  box-sizing: border-box;
  ${({ top, left }) => css`
    top: ${top}px;
    left: ${left}px;
  `}
  ul {
    box-sizing: border-box;
    padding: 10px;
    margin: 0;
    list-style: none;
  }
  ul li {
    padding: 18px 12px;
  }
  /* hover */
  ul li:hover {
    cursor: pointer;
    background-color: #000000;
  }
`;

现在,我们可以检查浏览器中的内容:

在 React 中创建自定义上下文菜单钩子

要创建自定义的右键单击上下文菜单 Hook,我们将创建一个名为 的文件,我们将在其中创建 Hook 函数。此外,我们将在名为 :useContextMenu.jscomponentsMenuContextHook.js

src/hooks/useContextMenu

import { useState, useEffect } from "react";
const useContextMenu = () => {
  const [clicked, setClicked] = useState(false);
  const [points, setPoints] = useState({
    x: 0,
    y: 0,
  });
  useEffect(() => {
    const handleClick = () => setClicked(false);
    document.addEventListener("click", handleClick);
    return () => {
      document.removeEventListener("click", handleClick);
    };
  }, []);
  return {
    clicked,
    setClicked,
    points,
    setPoints,
  };
};
export default useContextMenu;

In the Hook, we declare two states: and — just like in the previous section. Then, we use the Hook to register an event listener and clean up the event with the cleanup function. Lastly, , , , and are returned.useContexMenuclickedpointsuseEffectclickedsetClickedpoints``setPoints

In the file, we will make use of this Hook as follows:MenuContextHook.js

src/components/MenuContextHook.js
​
import React from "react";
import useContextMenu from "../hooks/useContextMenu";
import { ContextMenu } from "../styles/styles";
import Menu from "./Menu";
const MenuContextHook = ({ data }) => {
  const { clicked, setClicked, points, setPoints } = useContextMenu();
  return (
    <div>
      {data.map((item) => (
        <div
          onContextMenu={(e) => {
            e.preventDefault();
            setClicked(true);
            setPoints({
              x: e.pageX,
              y: e.pageY,
            });
            console.log("Right Click", e.pageX, e.pageY);
          }}
        >
          <Menu key={item.id} title={item.title} />
        </div>
      ))}
      {clicked && (
        <ContextMenu top={points.y} left={points.x}>
          <ul>
            <li>Edit</li>
            <li>Copy</li>
            <li>Delete</li>
          </ul>
        </ContextMenu>
      )}
    </div>
  );
};
export default MenuContextHook;

基本上,在这个文件中,我们正在重组 、、 和 从 Hook 并使用它创建自定义右键单击菜单,如上一节所述。clickedsetClickedpointssetPointsuseContextMenu

结论和考虑

本文介绍了在 React 应用程序中创建自定义上下文菜单的多种方法。请记住,如果你在 React 中创建自定义上下文菜单,请考虑移动交互。如果用户使用移动电话,则可能无法右键单击。

因此,您可能需要三思而后行,为什么需要自定义上下文菜单。如果用户想要查看默认菜单,可能会导致不良体验。

猜你喜欢

转载自blog.csdn.net/weixin_47967031/article/details/132737152