Обработка тяжелых ресурсов / DOM на портале React - PullRequest
0 голосов
/ 02 ноября 2019

Я создал реактивный портал внутри моего приложения, чтобы справиться с использованием Modal. Цель портала находится за пределами моего корневого элемента React, как брат моего корневого элемента.

<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, которые не влияют на общую производительность?

1 Ответ

0 голосов
/ 02 ноября 2019

Для этого может быть несколько причин:

  • Последний эффект всегда будет запускаться при каждом повторном рендеринге, поскольку props.children является объектом, и, следовательно, даже если одни и те же потомки были переданы снова, он 'будет новым объектом.
  • Прямые манипуляции с DOM - это анти-паттерн, так как React поддерживает несколько ссылок на DOM в памяти для быстрого сравнения, поэтому прямая мутация может привести к некоторому перфектному попаданию. Попробуйте написать то же самое в декларативномfashion.
  • Извлеките содержимое портала в другой подкомпонент и по возможности избегайте манипуляций с DOM.

Одно место будет:

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

может бытьзаменены в функции рендеринга как:

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