您的下一个 React Modal 具有您自己的“useModal”Hook 和 Context API。

2025-06-08

您的下一个 React Modal 具有您自己的“useModal”Hook 和 Context API。

大家好,本文将简要回顾如何在 React 项目中结合 Hooks、Context 和 Portals 使用 Modals 组件。你需要具备一定的 React 编程经验,并了解 React 的最新更新,例如 Hooks 和 Context API。让我们开始吧!

模态组件

在编写我们的 Modal 组件之前,让我们打开 public/index.html(或呈现 JS 代码的 HTML)并添加一个新标签以使用 React Portal 呈现 Modal 组件。

<body>
  <noscript>
    You need to enable JavaScript to run this app.
  </noscript>
  <div id="modal-root"></div>
  <div id="root"></div>
</body>
Enter fullscreen mode Exit fullscreen mode

现在让我们编写我们的模态组件,并使用 createPortal 函数,createPortal 函数需要两个参数,第一个是实际的 JSX,第二个是将要渲染它的 DOM 元素。

import React from "react";
import ReactDOM from "react-dom";

const Modal = () => {
  return ReactDOM.createPortal(
    <div
      className="fixed top-0 left-0 h-screen w-full flex items-center justify-center"
      style={{ background: "rgba(0,0,0,0.8)" }}
    >
      <div className="bg-white relative p-5 shadow-lg rounded flex flex-col items-start text-lg text-gray-800">
        <button
          className="absolute top-0 right-0 -mt-12 font-bold self-end rounded-full bg-red-200 mb-3 bg-white text-red-700 w-8 h-8"
          onClick={() => {}}
        >
          &times;
        </button>
        <p>I am the Modal</p>
      </div>
    </div>,
    document.querySelector("#modal-root")
  );
};

export default Modal;

Enter fullscreen mode Exit fullscreen mode

useModal 钩子

这个自定义 Hook 将保存我们的模态组件状态,但首先让我们根据 React 文档回顾一下 Hook 是什么:

React Hooks 是一些函数,让我们能够从函数组件中钩住 React 的状态和生命周期功能。这意味着,Hooks 允许我们轻松地操作函数组件的状态,而无需将其转换为类组件。

换句话说,Hooks 允许我们创建状态和方法的“可共享模型”来操作这些状态。通过返回两者,我们可以跨组件重用它们,并避免项目中的代码重复。如果我们有多个组件初始化相同的状态结构和方法,最好将它们提取到自定义 hook 中,这样我们就可以把状态和方法放在一个地方并重用。这就是我们自定义的useModal React Hook。

import React from "react";

export default () => {
  let [modal, setModal] = React.useState(false);
  let [modalContent, setModalContent] = React.useState("I'm the Modal Content");

  let handleModal = (content = false) => {
    setModal(!modal);
    if (content) {
      setModalContent(content);
    }
  };

  return { modal, handleModal, modalContent };
};

Enter fullscreen mode Exit fullscreen mode

通常,我们创建的每个 Hook 都需要以“use”开头。
现在你可能认为可以使用 Hook 跨组件共享实际状态值……遗憾的是,答案是否定的。每次在组件中使用 Hook 并从 Hook 中提取状态时,都会创建一个仅在该组件内可见的“本地状态”。如果要将该实际状态传递给子组件,则必须通过 props 或在本例中使用React Context来实现。

反应上下文

我们将在 ModalContext 中使用新创建的 React Hook...

import React from "react";
import useModal from "./useModal";
import Modal from "./modal";

let ModalContext;
let { Provider } = (ModalContext = React.createContext());

let ModalProvider = ({ children }) => {
  let { modal, handleModal, modalContent } = useModal();
  return (
    <Provider value={{ modal, handleModal, modalContent }}>
      <Modal />
      {children}
    </Provider>
  );
};

export { ModalContext, ModalProvider };
Enter fullscreen mode Exit fullscreen mode

现在让我们在模态组件中做一个简单的修改,开始使用我们的上下文信息作为道具。

import React from "react";
import ReactDOM from "react-dom";
import { ModalContext } from "./modalContext";

const Modal = () => {
  let { modalContent, handleModal, modal } = React.useContext(ModalContext);
  if (modal) {
    return ReactDOM.createPortal(
      <div
        className="fixed top-0 left-0 h-screen w-full flex items-center justify-center"
        style={{ background: "rgba(0,0,0,0.8)" }}
      >
        <div className="bg-white relative p-5 shadow-lg rounded flex flex-col items-start text-lg text-gray-800">
          <button
            className="absolute top-0 right-0 -mt-12 font-bold self-end rounded-full bg-red-200 mb-3 bg-white text-red-700 w-8 h-8"
            onClick={() => handleModal()}
          >
            &times;
          </button>
          <p>{modalContent}</p>
        </div>
      </div>,
      document.querySelector("#modal-root")
    );
  } else return null;
};

export default Modal;

Enter fullscreen mode Exit fullscreen mode

现在让我们转到 app.js 组件,并开始使用我们的 Modal 组件和Context Provider

import React from "react";
import { ModalProvider } from "./modalContext";
import Component from "./component";
import Component2 from "./component2";

export default function App() {
  return (
    <div className="App container mx-auto px-8 text-gray-700">
      <h1 className="text-3xl">Hello CodeSandbox</h1>
      <h2 className="text-xl mb-6">Start editing to see some magic happen!</h2>
      <ModalProvider>
        <Component />
        <Component2 />
      </ModalProvider>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

你会注意到那里有几个组件“Component 和 Component2”,它们是一些虚拟组件,带有一个按钮来打开 Modal,它们之间的主要区别在于要在 Modal 中呈现的消息

import React from "react";
import { ModalContext } from "./modalContext";

const Component = () => {
  let { handleModal } = React.useContext(ModalContext);

  return (
    <>
      <p>
        Lorem ipsum dolor sit amet consectetur, adipisicing elit. Cumque quidem
        asperiores?
      </p>
      <button
        className="mt-6 rounded  bg-purple-700 text-purple-100 px-5 h-12"
        onClick={() => handleModal("This is component modal content")}
      >
        open this modal!
      </button>
    </>
  );
};

export default Component;
Enter fullscreen mode Exit fullscreen mode

你最终会得到类似这个CodeSandbox Modal Demo 的内容

就是这样,我试图使其尽可能简短,而不深入研究代码的具体部分,如果您对代码有任何疑问或有不同的方法,请在评论中告诉我。

图片来源:Pexels 上的 Rodolpho Zanardo

对于 Rhys Nicholls 的“从内部关闭模式”,
您可以将组件传递给 handleModal 函数而不是字符串,然后在该组件中,您可以从上下文中解构 handleModal 函数并根据需要调用该函数,就像这样...
组件

function ContentComponent() {
  let { handleModal } = React.useContext(ModalContext);
  return (
    <>
      <p>Hello here !!!</p>
      <button
        className="h-8 px-3 text-white bg-red-500 text-xs rounded"
        onClick={handleModal}
      >
        Close modal
      </button>
    </>
  );
}

Enter fullscreen mode Exit fullscreen mode

然后导入此组件并在 handleModal 中使用它

const Component = () => {
  let { handleModal } = React.useContext(ModalContext);

  return (
    <>
      <p>
        Lorem ipsum dolor sit amet consectetur, adipisicing elit. Cumque quidem
        asperiores?
      </p>
      <button
        className="mt-6 rounded  bg-purple-700 text-purple-100 px-5 h-12"
        onClick={() => handleModal(<ContentComponent />)}
      >
        open this modal!
      </button>
    </>
  );
};

Enter fullscreen mode Exit fullscreen mode

您可以在此处看到实例https://codesandbox.io/s/eloquent-hamilton-vgbyq?file=/src/component.js:75-508https://codesandbox.io/s/eloquent-hamilton-vgbyq?file=/src/component.js:75-508

对于 Joel Robles Bentham 来说,“在页面加载时打开模式”
简单地在组件安装时调用它,使用和效果如下

// the string could be a component as as well
 React.useEffect(() => {
    handleModal("This is component 2 modal content on page load");
  }, []);
Enter fullscreen mode Exit fullscreen mode

此处的实例https://codesandbox.io/s/eloquent-hamilton-vgbyq?file=/src/component2.js:160-261

链接:https://dev.to/alexandprivate/your-next-react-modal-with-your-own-usemodal-hook-context-api-3jg7
PREV
开发者免费 - 软件列表(SaaS、PaaS、IaaS 等)目录
NEXT
仅使用 CSS 创建网站主题切换器