0

我正在尝试创建一个弹出对话框以在用户单击按钮时显示。为此,我正在使用门户网站。

我希望它看起来像下面的图片, 在此处输入图像描述

所以基本上,当用户点击添加按钮时,我希望弹出对话框显示如上图所示。

在弹出组件中,我想用孩子渲染叠加层。当用户点击覆盖 div 时,弹出窗口应该关闭。

我有一些不使用 Portal 就可以工作的东西,如下所示,

下面是我没有使用 Portal 的代码,

function Parent({isDialogOpen, setDialogOpen, setSomething}: Props) {
    const [isClicked, setIsClicked] = React.useState(false);
    const handleButtonClick = () => {
        if (setIsDialogOpen) setIsDialogOpen(!isDialogOpen);
        if (setSomething) setSomething(isDialogOpen);
        setIsClicked(!isClicked);
    };
    return (
        <button onClick={handleButtonClick}>click</button>
        {isDialogOpen && isClicked &&
            <Overlay>
                <Dialog>
                    //some divs
                </Dialog>
            </Overlay>
        }
    );
}


const Overlay = styled.div`
    position: fixed;
    padding-top:60px;
    bottom: 40px;
    top: 0;
    left: 0;
    right: 0;
    backdrop-filter: blur(8px);
    z-index: 100;
    display: flex;
    justify-content: center;
    align-items: center;
`;

 const Dialog = styled.div`
     padding: 16px;
     width: 384px;
     max-height: calc(100% - 200px);
     display: flex;
     flex-direction: column;
 `;

现在我正在使用下面的门户重写上面,

function Parent({isDialogOpen, setDialogOpen, setSomething}: Props) {
    const [isClicked, setIsClicked] = React.useState(false);
    const handleButtonClick = () => {
        if (setIsDialogOpen) setIsDialogOpen(!isDialogOpen);
        if (setSomething) setSomething(isDialogOpen);
        setIsClicked(!isClicked);
    };
    return (
        <button onClick={handleButtonClick}>click</button>
        {isDialogOpen && isClicked &&
            <Popup setSomething={setSomething} setIsDialogOpen={setIsDialogOpen} setIsClicked= 
            {setIsClicked}>
                <Dialog>
                    //some divs
                </Dialog>
            </Overlay>
        }
    );
}

function Popup({setIsClicked, setSomething, setIsDialogOpen, children}: Props) {
    return ReactDom.createPortal(
        <>
            <Overlay
                onClick={() => {
                    if (setIsDialogOpen) setIsDialogOpen(false);
                    if (setSomething) setSomething(true);
                    setIsClicked(false);
                }}
            >
                {children}
            </Overlay>
        </>,
        //dont know what to pass here
    );
}

基本上如上图所示,我想用对话框渲染叠加层。

现在在弹出组件中,我想使用类名“popup”创建 div,并找到具有类导航栏的 div 元素并将此 div 弹出窗口附加到导航栏 div

并在 reactDOM.createPortal 中使用弹出类传递这个 div 元素。

我是新手,不知道该怎么做。有人可以帮我解决这个问题。

谢谢。

4

1 回答 1

0

正如我在评论中向您提到的,react 不会为您创建 dom 节点。你必须自己做。你怎么做取决于你的需要。我能想到的最基本的例子如下:

  • 当组件第一次挂载时,我们需要创建门户并将其插入document.body
  • 一旦我们确定门户存在,我们就可以使用我们的 dom ref 渲染到门户中。

function Popup({setIsClicked, setSomething, setIsDialogOpen, children}: Props) {

  const [portal,setPortal] = React.useState<HTMLDivElement|null>( (document.getElementById('my-portal') as HTMLDivElement)||null);
  const createPortalIfNotExists = React.useCallback(()=>{
    if(portal===null){
      const el = document.createElement('div');
      el.id='my-portal';
      document.body.appendChild(el);
      // switch this line for the one above if you want it to be first in tree
      // document.body.insertBefore(el, document.body.firstChild);
      setPortal(document.getElementById('my-portal') as HTMLDivElement);
    }
  },[portal]);

  createPortalIfNotExists();

  if(portal===null){
    return null;
  }

  return ReactDom.createPortal(
    <>
        <Overlay
            onClick={() => {
                if (setIsDialogOpen) setIsDialogOpen(false);
                if (setSomething) setSomething(true);
                setIsClicked(false);
            }}
        >
            {children}
        </Overlay>
    </>,
    portal
  );
}

这只是一种可能的方式。还有其他更高级的用例,您可以让组件树中的某个其他元素呈现门户。但这应该足以让您入门。另外,我没有测试过这个,因为我在这台机器上没有 tsc/tslint 所以 YMMV

于 2020-10-27T13:42:36.617 回答