React组件二次挂载useEffect不更新container 无法打开window.open窗口
问题原因
二次挂载时弹窗无法打开的核心问题有3个:
- 第一次打开弹窗时,你把页面上预存的
id="portal"节点append到了新窗口的body中,组件卸载关闭弹窗后,这个节点已经脱离原页面DOM树,二次挂载时执行document.getElementById('portal')只能拿到null,container状态无法被赋值为有效DOM节点 - 容器节点完全依赖页面预渲染,没有主动创建、销毁的逻辑,节点被移走后没有兜底重建机制
window.open放在useEffect中执行属于异步调用,脱离用户点击的同步执行链,二次触发时大概率被浏览器安全策略拦截。
修复代码
import { useState, useRef, useEffect } from "react"; import { createPortal } from "react-dom"; import New_Window from "./New_Window"; function RenderInWindow({ handleOpen }) { const [container, setContainer] = useState(null); const newWindow = useRef(null); // 组件挂载时主动创建容器节点,不依赖页面预存节点 useEffect(() => { // 先清掉可能残留的旧portal节点 const oldPortal = document.getElementById('portal'); if (oldPortal) oldPortal.remove(); // 创建新的容器节点 const div = document.createElement('div'); div.setAttribute('id', 'portal'); document.body.appendChild(div); setContainer(div); // 组件卸载时同步销毁容器、关闭窗口 return () => { if (newWindow.current && !newWindow.current.closed) { newWindow.current.close(); } div.remove(); setContainer(null); handleOpen?.(); } }, []); // 容器就绪后打开新窗口、挂载内容 useEffect(() => { if (!container) return; // 打开新窗口 const curWindow = window.open( "", "", "width=810,height=520,left=300,top=300" ); // 兼容弹窗被拦截的场景 if (!curWindow) { alert('弹窗被浏览器拦截,请允许弹窗权限后重试'); return; } newWindow.current = curWindow; // 复制父页面样式到新窗口 const parentHeadChildNodes = window.document.head.childNodes; parentHeadChildNodes.forEach(item => { curWindow.document.head.appendChild(item.cloneNode(true)); }); // 把容器挂载到新窗口body curWindow.document.body.appendChild(container); }, [container]); const handleClose = () => { if (newWindow.current && !newWindow.current.closed) { newWindow.current.close(); } setContainer(null); handleOpen?.(); } return container && createPortal( <New_Window handleClose={handleClose} />, container ) }; export default RenderInWindow;
关键修复点
- 去掉对页面预存
#portal节点的依赖,组件每次挂载时主动创建新的容器节点,挂载前先清理可能残留的旧节点,从根源避免getElementById拿到null的问题 - 补全组件卸载时的销毁逻辑:关闭新窗口、移除临时创建的容器节点、重置状态,避免内存泄漏和DOM残留
- 增加弹窗拦截兜底判断,避免权限问题导致的JS报错
- 补全React hooks的导入,修正原代码漏引入
useState/useRef/useEffect的问题
内容的提问来源于stack exchange,提问作者A Chasnyk
相关产品推荐
相关产品推荐

