Реагировать на вызов createPortal не удается, потому что document.body не существует? - PullRequest
0 голосов
/ 17 февраля 2020

У меня есть компонент React для модального диалога. Это прекрасно работает, когда я не делаю с ним ничего особенного:

return (
    <Modal className={isShowing ? "modal is-active" : "modal"}>
        <ModalBackground></ModalBackground>
        <ModalCard>
            <ModalHeader>
                <ModalTitle>
                    <NoticeIcon type={noticeType}/>
                    {title}
                </ModalTitle>
                <ModalClose onClick={handleHideModal}></ModalClose>
            </ModalHeader>
            <ModalContent>
                {content}
            </ModalContent>
            <ModalFoot>
                { buttonSpecs.map((spec) => 
                    <BottomButton key={spec.label} label={spec.label} handleClick={spec.handleClick} isPrimary={spec.isPrimary} />
                )}
            </ModalFoot>
        </ModalCard>
    </Modal>
);

Тем не менее, предписанный подход к отображению диалогового окна состоит в том, чтобы отображать его внизу документа DOM, а не вклинивать его внутри компонент вызывает его, в том числе тот, в который внешний элемент DOM компонента может даже не быть разрешен. (Действительно, в одном случае я вызываю модальное диалоговое окно из компонента, который отображает строку таблицы, в TR. Это «работает», но выдает сообщение об ошибке на консоль Chrome, как ничего, кроме TH и TD разрешены внутри ТР). Это можно сделать с помощью ReactDOM.createPortal:

import ReactDOM from 'react-dom';
...
return (ReactDOM.createPortal(
    <Modal className={isShowing ? "modal is-active" : "modal"}>
        <ModalBackground></ModalBackground>
        <ModalCard>
            <ModalHeader>
                <ModalTitle>
                    <NoticeIcon type={noticeType}/>
                    {title}
                </ModalTitle>
                <ModalClose onClick={handleHideModal}></ModalClose>
            </ModalHeader>
            <ModalContent>
                {content}
            </ModalContent>
            <ModalFoot>
                { buttonSpecs.map((spec) => 
                    <BottomButton key={spec.label} label={spec.label} handleClick={spec.handleClick} isPrimary={spec.isPrimary} />
                )}
            </ModalFoot>
        </ModalCard>
    </Modal>
), document.body);

. Вызов ReactDOM.createPortal со вторым параметром document.body должен визуализировать компонент в конце тела. Но это выдает мне ошибку: «Целевой контейнер не является элементом DOM» .

В то время, когда я показываю диалог, страница и все остальное в нем уже существует, поэтому тело существует.

Я попробовал другой объясненный подход, который заключается в создании явного div в конце внешней HTML структуры документа в качестве контейнера для модального:

<div id="root"></div>
<div id="modalRoot"></div>

и затем замените document.getElementById('modalRoot') на document.body. Но это дает мне ту же ошибку.

Единственные ответы, которые я нашел для этого, включают случаи, когда модал вызывается при первом рендеринге скриптом, который находится в разделе HEAD документа, так что тело не существует Совет в тех случаях состоял в том, чтобы переместить сценарий в конец документа. В моем случае диалоговое окно отображается в ответ на некоторые мои действия внутри документа, который уже был обработан.

Есть идеи?

1 Ответ

2 голосов
/ 17 февраля 2020
import ReactDOM from 'react-dom';
...
return ReactDOM.createPortal(
    <Modal className={isShowing ? "modal is-active" : "modal"}>
        <ModalBackground></ModalBackground>
        <ModalCard>
            <ModalHeader>
                <ModalTitle>
                    <NoticeIcon type={noticeType}/>
                    {title}
                </ModalTitle>
                <ModalClose onClick={handleHideModal}></ModalClose>
            </ModalHeader>
            <ModalContent>
                {content}
            </ModalContent>
            <ModalFoot>
                { buttonSpecs.map((spec) => 
                    <BottomButton key={spec.label} label={spec.label} handleClick={spec.handleClick} isPrimary={spec.isPrimary} />
                )}
            </ModalFoot>
        </ModalCard>
    </Modal>
), document.body);

Ваша функция должна возвращать объект, возвращенный функцией ReactDOM.createPortal. Вы добавили дополнительные ( в начале функции возврата. См .: https://reactjs.org/docs/portals.html#usage

...