这是主页。
"use client";
import React, { useEffect, useState } from "react";
import TestModal from "./TestModal";
const App = () => {
const [isOpen, setIsOpen] = useState(false);
const [isDiscardModalOpen, setIsDiscardModalOpen] = useState(false);
useEffect(() => {
const handleEscape = (event: KeyboardEvent) => {
if (event.key === "Escape" && !isDiscardModalOpen) {
setIsDiscardModalOpen(true);
}
};
document.addEventListener("keydown", handleEscape);
return () => {
document.removeEventListener("keydown", handleEscape);
};
}, [isDiscardModalOpen]);
return (
<div>
<button onClick={() => setIsOpen(true)}>Open Modal</button>
<TestModal
title="Test Modal"
isOpen={isOpen}
onClose={() => setIsOpen(false)}
>
<div>This is a test Modal</div>
</TestModal>
<TestModal
title="Discard Modal"
isOpen={isDiscardModalOpen}
onClose={() => {
setIsDiscardModalOpen(false);
}}
>
<div>This is a discard Modal</div>
</TestModal>
</div>
);
};
export default App;
这是一个模态组件。
"use client";
import React, { useEffect, useRef } from "react";
interface ModalProps {
isOpen: boolean;
title: string;
children: React.ReactNode;
onClose: () => void;
}
const TestModal: React.FC<ModalProps> = ({
isOpen,
title,
children,
onClose,
}) => {
const modalRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (
isOpen &&
modalRef.current &&
!modalRef.current.contains(event.target as Node)
) {
event.preventDefault();
}
};
const handleEscape = (event: KeyboardEvent) => {
if (event.key === "Escape" && isOpen) {
onClose();
}
};
if (isOpen) {
document.addEventListener("keydown", handleKeyDown);
document.addEventListener("keydown", handleEscape);
}
return () => {
document.removeEventListener("keydown", handleKeyDown);
document.removeEventListener("keydown", handleEscape);
};
}, [isOpen, onClose]);
if (!isOpen) return null;
return (
<>
<div
role="dialog"
aria-modal="true"
ref={modalRef}
className="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center p-4"
>
<div className="bg-white p-6 rounded-lg shadow-lg max-w-lg w-full">
<header className="flex justify-between items-center mb-4">
<h2 className="text-xl font-semibold">{title}</h2>
<button
onClick={onClose}
className="text-gray-600 hover:text-gray-800"
>
Close
</button>
</header>
<div className="modal-content overflow-auto max-h-[80vh]">
{children}
</div>
</div>
</div>
</>
);
};
export default TestModal;
我有一个页面和一个 TestModal 组件。我的页面上有一个 esc 按钮功能,点击 esc 将打开丢弃模式。我在 testModal 组件中也有转义功能。当模式打开时,按 esc 关闭模式。我遇到的问题是,当模式打开并且我按下 esc 键时,页面上的 esc 按钮事件会在模式 esc 事件之上触发。所以现在有两个模式打开,并且丢弃模式在 testModal 之上打开。这不应该发生。从模式中按 esc 键应该只会关闭模式,而不会将单击事件传播到父级。我怎样才能做到当模式打开时,其所有父组件的所有键盘和鼠标事件都被禁用?
自己尝试以获得更好的上下文: Codesandbox链接
如果我没记错的话,您希望仅在没有打开的模态时才出现丢弃模态,对吗?所以这应该有帮助:
App.tsx:
useEffect(() => {
const handleEscape = (event: KeyboardEvent) => {
if (event.key === "Escape" && !isDiscardModalOpen && !isOpen) {
setIsDiscardModalOpen(true);
}
};
document.addEventListener("keydown", handleEscape);
return () => {
document.removeEventListener("keydown", handleEscape);
};
}, [isDiscardModalOpen, isOpen]);
TestModal.tsx:
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape" && isOpen) {
onClose();
}
};
document.addEventListener("keydown", handleKeyDown);
return () => {
document.removeEventListener("keydown", handleKeyDown);
};
}, [isOpen, onClose]);
useEffect
中的App.tsx
只会在setIsDiscardModalOpen
状态为isOpen
时调用false
。我已从 event.stopPropagation
中删除了 TestModal.tsx
,因为它不再需要了。