Javascript 如何在使用ReactDOM.createPortal()的子级上处理ref.current.contains()?
我使用React钩子创建了一个应用程序。我有一个助手函数Javascript 如何在使用ReactDOM.createPortal()的子级上处理ref.current.contains()?,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,我使用React钩子创建了一个应用程序。我有一个助手函数onClickOutsideHook(ref,callback),当您使用React在提供ref的组件外部单击时,它会触发回调 export const onClickOutsideHook=(ref,callback)=>{ //钩住https://stackoverflow.com/a/42234988/8583669 React.useffect(()=>{ const handleClickOutside=事件=>{ if(ref?
onClickOutsideHook(ref,callback)
,当您使用React在提供ref
的组件外部单击时,它会触发回调
export const onClickOutsideHook=(ref,callback)=>{
//钩住https://stackoverflow.com/a/42234988/8583669
React.useffect(()=>{
const handleClickOutside=事件=>{
if(ref?current&!ref.current.contains(event.target)){
回调();
}
};
//绑定事件侦听器
文件。添加了文本列表(“鼠标向下”,手动单击外部);
return()=>{
//在清理时取消绑定事件侦听器
文档。移除EventListener(“鼠标向下”,把手单击外部);
};
},[callback,ref]);
};
我有一个组件下拉列表
,它使用此帮助器,因此当您在其外部单击时,它将关闭。此组件有一个Modal
组件作为使用ReactDOM.createPortal
的子组件。我用它来渲染主体中的模式
,这样它就可以覆盖所有的应用程序屏幕。MyModal
包含一个按钮,当您单击该按钮时,该按钮会提示消息:
函数模态(){
返回ReactDOM.createPortal(
警报(“单击模式”)}>单击
,
文件正文
);
}
功能下拉列表(道具){
const[isModalOpen,setIsModalOpen]=React.useState(false);
const dropdownRef=React.useRef(null);
onclick外侧钩(dropdownRef,props.onClose);
返回(
单击外部,我将关闭
setIsModalOpen(真)}>打开模式
{isModalOpen?:}
);
}
问题是,当我单击模式
按钮触发警报时,下拉列表
在之前关闭,因为我在其外部单击(模式
不是作为下拉列表
的子项呈现,而是在正文
中)。所以我的警报永远不会被触发
是否有一种方法可以使用ref
将Modal
定义为下拉列表的子项
,但仍然使用ReactDOM.createPortal
在正文中呈现它
只需查看。作为一种解决方法,您可以在模态中添加ID属性,然后检查单击是否在模态之外
function Modal() {
return ReactDOM.createPortal(
<div id="modalId">
<button onClick={() => alert("Clicked on Modal")}>Click</button>
</div>,
document.body
);
}
就像医生说的:
尽管门户可以位于DOM树中的任何位置,但它在其他方面的行为都类似于普通的React子级
这包括事件冒泡。从门户内部激发的事件将
传播到树中的祖先,即使
元素不是DOM树中的祖先
但情况并非如此,在文档中添加了mousedown事件侦听器,而不仅仅是下拉列表组件。即便如此,泡沫仍在发生
这意味着,如果将ref添加到模式,然后在mousedown事件上添加事件侦听器以停止传播,则永远不会调用handleClickOutside函数
这似乎仍然是一个解决办法,我不知道是否有一个适当的方法来检查
function Modal() {
const modalRef = useRef();
useEffect(() => {
const stopPropagation = e => {
e.stopPropagation();
};
const { current: modalDom } = modalRef;
modalDom.addEventListener("mousedown", stopPropagation);
return () => {
modalDom.removeEventListener("mousedown", stopPropagation);
};
}, []);
return ReactDOM.createPortal(
<div
ref={modalRef}
style={{
position: "absolute",
top: 0,
left: 0,
height: "100%",
width: "100%",
background: "rgba(0,0,0,0.6)"
}}
>
<button onClick={() => alert("Clicked on Modal")}>Click</button>
</div>,
document.body
);
}
函数模态(){
const modalRef=useRef();
useffect(()=>{
常数stopPropagation=e=>{
e、 停止传播();
};
const{current:modalDom}=modalRef;
modalDom.addEventListener(“鼠标向下”,停止播放);
return()=>{
modalDom.removeEventListener(“鼠标下移”,停止播放);
};
}, []);
返回ReactDOM.createPortal(
警报(“单击模式”)}>单击
,
文件正文
);
}
观看下面的模态组件。嘿,谢谢你的想法,但正如你所说的,这只是一个解决办法;)向上投票感谢您的帮助它工作得非常完美,这是一个很好的解决方法,因为它只会影响Modal
组件
function Modal() {
const modalRef = useRef();
useEffect(() => {
const stopPropagation = e => {
e.stopPropagation();
};
const { current: modalDom } = modalRef;
modalDom.addEventListener("mousedown", stopPropagation);
return () => {
modalDom.removeEventListener("mousedown", stopPropagation);
};
}, []);
return ReactDOM.createPortal(
<div
ref={modalRef}
style={{
position: "absolute",
top: 0,
left: 0,
height: "100%",
width: "100%",
background: "rgba(0,0,0,0.6)"
}}
>
<button onClick={() => alert("Clicked on Modal")}>Click</button>
</div>,
document.body
);
}