在模态上按 Escape 键会触发父组件上的 escape 事件

问题描述 投票:0回答:1

这是主页。

"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链接

javascript reactjs events modal-dialog keyboard-events
1个回答
0
投票

如果我没记错的话,您希望仅在没有打开的模态时才出现丢弃模态,对吗?所以这应该有帮助:

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
,因为它不再需要了。

© www.soinside.com 2019 - 2024. All rights reserved.