在按钮点击时,我希望Modal
出现。Modal
组件添加了一个eventListener
,因此当你在模态之外点击时,它会关闭。在React 18中,click事件会触发,因为按钮点击发生在之前Modal被渲染?如果我更改为react 17,这不会发生。
找到一个CodeSandbox here。请注意,当您单击按钮时,show
的状态设置为true。然后Modal
组件直接呈现并调用close函数。
App.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>
);
}
Modal.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>
);
}
4条答案
按热度按时间gwbalxhn1#
您可以调用event.stopPropagation来防止多个单击处理程序捕获同一个单击事件。
我不知道为什么React 17和18之间会有不同。React使用自己的“合成事件”,两个版本之间的事件传播/冒泡方式可能会发生变化。
它可能与React 18中所谓的“自动批处理”有关。https://github.com/reactwg/react-18/discussions/21
在您的示例中,Modal组件使用
document.addEventlistener()
的本地事件处理。看起来React 18处理应用程序内部的点击,这会触发状态更改和重新呈现,挂载Modal
组件,运行useEffect()
钩子,并在点击事件传播到窗口节点之前创建新的事件侦听器。在React 17中,事件大概在重新呈现发生之前完成传播。68de4m5k2#
Dan Abramov在这篇评论中给出了一些建议(https://github.com/facebook/react/issues/24657#issuecomment-1150119055)。
建议一是:在useEffect中,使用setTimeout延迟addEventListener调用,等待时间为0。示例:
建议2是捕获当前事件并忽略模态的useEffect中的事件。
qij5mzcb3#
另一种解决方案涉及新的
startTransition
API:https://codesandbox.io/s/gracious-benz-0ey6ol?file=/src/App.js:340-388这个新的API(doc)肯定有帮助,但我不确定这是一个合法的解决方案,文档对此也不太清楚。如果React的人能发表评论就好了-这是滥用吗?
2nc8po8w4#
你可以在
handleOutsideClick
中修改你的if
语句,如下所示。你的不工作是因为打开Modal
的button
,因为它不在Modal
中,传递了你的if(!ref?.current?.contains(e.target))
。CodeSandbox的Here is a working fork。我将按钮的
ref
传递给Modal
。以下是我更改的内容: