在React Portal上处理大量资源/ DOM

时间:2019-11-02 08:41:10

标签: reactjs react-portal

我已经在我的应用程序内部创建了一个react门户来处理Modal的使用。门户网站目标是我的根元素的兄弟姐妹,位于我的React root 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>

因此,我的Portal内容呈现在react应用程序之外,并且工作正常。这是我的反应门户代码

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操作而不会影响整体性能?

1 个答案:

答案 0 :(得分:0)

原因因素可以归因于此:

  • 由于props.children是一个对象,因此每次重新渲染都将始终运行最后一个效果,因此,即使再次传递相同的子代,它也将是一个新对象。
  • 直接DOM操作是一种反模式,因为React在内存中维护了多个DOM引用以进行快速区分,因此直接突变可能会导致一些性能下降。尝试以声明性方式编写同样的内容。
  • 将门户网站内容提取到另一个子组件中,并尽可能避免DOM操作。

一个地方是:

if (_.size(props.children) <= 0) {
  modalInnerRoot.removeChild(div.current);
}

可以在渲染函数中替换,例如:

{React.Children.count(props.children) ? <div /> : null}
相关问题