首页 > 解决方案 > React - 带有 `document.body` 的`createPortal` 导致点击事件立即触发

问题描述

我的应用程序有一个名为Open Modal. 单击此按钮时,将向document.bodyusing附加一个模式createPortal演示链接

一旦安装了模式(即isOpen === true),我想在每次用户单击页面上的某个位置时将一些文本打印到控制台。

为此,我使用useEffect钩子将点击事件绑定到document对象,如果isOpen === true

useEffect(() => {
    const eventHandler = () => console.log("You clicked me");

    if (isOpen) {
      document.addEventListener("click", eventHandler);
    }

  }, [isOpen]);

当用户第一次点击“Open Modal”时,isOpenfalse变为true,这会导致组件重新渲染。重新渲染完成后,注册上述效果中的点击事件。

问题

第一次单击 'Open Modal' 会document触发 'click 事件(您可以在控制台中看到事件处理程序的输出)。这对我来说没有意义 -当第一次“打开模式”点击传播到它时,点击事件怎么可能已经注册?document

预期行为

第一次点击 'Open Modal' 只会导致document'click 事件被注册,此时事件本身不应触发。

如果我省略createPortal(参见演示的第 38 行),应用程序将按预期工作,即在第一次单击“打开模式”时注册事件,并在页面上的所有后续单击时调用事件处理程序。

如果我将模式安装到应用程序根目录的同级而不是document.body(参见演示的第 39 行),该应用程序也可以按预期工作。所以上面描述的问题只有在createPortal使用 withdocument.body作为容器时才会出现,但我不明白为什么。

标签: reactjsreact-hooksevent-handlingevent-propagationreact-portal

解决方案


我不确定,但我认为,问题的发生是因为Event Propagation,在传播完成时,文档click事件已被注册,这可能是它调用eventHandler. 如果我错了,请纠正我。

如果您停止事件传播,它工作正常。

const openModal = useCallback(
    function (e) {
      if (!isOpen) { // stopping the propagation
        e.stopPropagation();
      }
      setOpen(true);
    },
    [setOpen, isOpen]
  );

演示链接在这里


推荐阅读