您的下一个 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>
现在让我们编写我们的模态组件,并使用 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={() => {}}
>
×
</button>
<p>I am the Modal</p>
</div>
</div>,
document.querySelector("#modal-root")
);
};
export default Modal;
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 };
};
通常,我们创建的每个 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 };
现在让我们在模态组件中做一个简单的修改,开始使用我们的上下文信息作为道具。
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()}
>
×
</button>
<p>{modalContent}</p>
</div>
</div>,
document.querySelector("#modal-root")
);
} else return null;
};
export default Modal;
现在让我们转到 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>
);
}
你会注意到那里有几个组件“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;
你最终会得到类似这个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>
</>
);
}
然后导入此组件并在 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>
</>
);
};
对于 Joel Robles Bentham 来说,“在页面加载时打开模式”
简单地在组件安装时调用它,使用和效果如下
// the string could be a component as as well
React.useEffect(() => {
handleModal("This is component 2 modal content on page load");
}, []);
此处的实例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