React,单击外部事件在单击打开后立即发生,从而阻止模式显示

Ben*_*nny 8 javascript reactjs

单击按钮时我希望Modal出现。该Modal组件添加了一个eventListener,因此当您在模态框外部单击时它会关闭。在 React 18 中,点击事件触发是因为按钮点击发生在Modal 渲染之前?如果我改为react 17,这种情况就不会发生。

在这里找到 CodeSandbox 。请注意,当您单击按钮时,show状态设置为 true。然后Modal组件渲染并直接调用close函数。

应用程序.js:

import { useState } from "react";
import Modal from "./Modal";
import "./styles.css";

export default function App() {
  const [show, setShow] = useState(false);

  const close = () => {
    setShow(false);
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <button
        onClick={(e) => {
          setShow(true);
        }}
      >
        Click
      </button>
      {show && <Modal close={close} />}
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

模态.js

import "./styles.css";
import { useRef, useEffect } from "react";

export default function Modal({ close }) {
  const ref = useRef(null);

  useEffect(() => {
    const handleOutsideClick = (e) => {
      if (!ref?.current?.contains(e.target)) {
        console.log("This one gets called because of the button click", e);
        close();
      }
    };

    document.addEventListener("click", handleOutsideClick, false);
    return () => {
      document.removeEventListener("click", handleOutsideClick, false);
    };
  }, [close]);

  return (
    <div ref={ref} className="Modal">
      <h1>I'm a Modal!</h1>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

Håk*_*Lid 7

您可以调用event.stopPropagation来防止多个单击处理程序捕获相同的单击事件。

    onClick={(e) => {
      e.stopPropagation();
      setShow(true);
    }}
Run Code Online (Sandbox Code Playgroud)

我不知道为什么 React 17 和 18 之间会有所不同。React 使用自己的“合成事件”,并且两个版本之间事件传播/冒泡的发生方式可能有所变化。

它可能与 React 18 中所谓的“自动批处理”有关。 https://github.com/reactwg/react-18/discussions/21

在您的示例中,模态组件使用本机事件处理与document.addEventlistener(). React 18 似乎处理应用程序内部的单击,这会触发状态更改和重新渲染,安装组件Modal,运行挂钩useEffect(),并在单击事件传播到窗口节点之前创建新的事件侦听器。在 React 17 中,事件可能在重新渲染发生之前完成传播。

  • hm 有点惊讶事件监听器捕获了按钮单击,因为单击发生在监听器注册之前 (2认同)