reactjs - 在 React Portal 上处理大量资源/DOM
问题描述
我在我的应用程序中创建了一个反应门户来处理 Modal 的使用。门户目标在我的 React 根 div 之外,作为我的根元素的兄弟。
<html>
<body>
<div id="root">{{app.html}}</div>
<div id="modal-root">
<div class="modal" tabIndex="-1" id="modal-inner-root" role="dialog">
</div>
</div>
</body>
</html>
所以我的门户内容呈现在反应应用程序之外并且它工作正常。这是我的反应门户代码
const PortalRawModal = (props) => {
const [ display, setDisplay ] = useState(document.getElementById("modal-inner-root").style.display)
const div = useRef(document.createElement('div'))
useEffect(()=> {
const modalInnerRoot = document.getElementById("modal-inner-root")
if(validate(props.showModalId)) {
if( props.showModalId == props.modalId && _.size(props.children) > 0 ) {
setDisplay("block");
if(_.size(modalInnerRoot.childNodes) > 0) {
modalInnerRoot.replaceChild(div.current,modalInnerRoot.childNodes[0]);
} else {
modalInnerRoot.appendChild(div.current);
}
div.current.className = props.modalInner;
document.getElementById("modal-root").className = props.modalClassName;
document.body.className = "modal-open";
} else {
document.getElementById("modal-root").className = props.modalClassName;
if(div.current.parentNode == modalInnerRoot) {
modalInnerRoot.removeChild(div.current);
div.current.className = "";
}
}
} else {
setDisplay("none");
document.getElementById("modal-root").className = "";
if(div.current.parentNode == modalInnerRoot) {
modalInnerRoot.removeChild(div.current).className = "";
}
document.body.className = "";
}
},[ props.showModalId ])
useEffect(()=> {
document.body.className = display == "none" ? "" : "modal-open";
document.getElementById("modal-inner-root").style.display = display;
return () => {
if(!validate(props.showModalId)) {
document.body.className = "";
document.getElementById("modal-inner-root").style.display = "none"
}
};
},[ display])
useEffect(()=> {
if(_.size(props.children) <= 0){
modalInnerRoot.removeChild(div.current)
document.body.className = "";
document.getElementById("modal-inner-root").style.display = "none";
}
return () => {
if(_.size(props.children) <= 0){
modalInnerRoot.removeChild(div.current)
document.body.className = "";
document.getElementById("modal-inner-root").style.display = "none";
}
}
},[props.children, props.showModalId])
return ReactDOM.createPortal(props.children ,div.current);
}
每当传递孩子并挂载模态框时,都会毫不拖延地绘制沉重的 DOM。但是相同的标记需要时间,甚至会使浏览器选项卡崩溃。我在处理繁重的 DOM 操作时哪里出错了?或者有什么async
方法可以处理不会影响整体性能的繁重 DOM 操作?
解决方案
有几个原因可以归因于此:
- 最后一个效果将始终作为对象在每次重新渲染时运行
props.children
,因此即使再次传递相同的子对象,它也将是一个新对象。 - 直接 DOM 操作是一种反模式,因为 React 在内存中维护了几个 DOM 引用以便快速区分,因此直接突变可能会导致一些性能命中。尝试以声明性方式编写相同的内容。
- 将门户内容提取到另一个子组件中,并尽可能避免 DOM 操作。
一个地方是:
if (_.size(props.children) <= 0) {
modalInnerRoot.removeChild(div.current);
}
可以在渲染函数中替换,例如:
{React.Children.count(props.children) ? <div /> : null}
推荐阅读
- vector - 有没有办法在 Rust 中从 1 开始索引向量?
- sql - SQL需要连接表和返回值的条件
- regex - notepad++ 正则表达式查找、替换和保持匹配
- javascript - 从 json 数组中删除特定元素
- r - 如何让cowplot小网格线成为标准ggplot默认软灰色,而不是纯黑色
- android - 如何在 Kotlin 中移动视图
- java - scala classOf[] 的 java 等价物
- sqlalchemy - 如何修复“位置参数”以成功运行 PostgreSQL 的人口普查 API?
- graphql - 是否可以防止在组件状态更改/重新渲染时重新获取“useLazyQuery”查询?
- python - 有没有办法让 python 在找到字符串时启动 match 语句,以便循环不会从头开始?