這是我參與8月更文挑戰的第6天,活動詳情查看:8月更文挑戰css
React 實現對話框有兩種方案:html
在實際開發中,咱們大多會選擇直接使用 Antd 的對話框組件,其實 Antd 對話框的實現也是基於 React Portals 這個特性,因此瞭解它能夠讓咱們的漂浮層之路走的更寬哦:react
Portal
是 React 16.3 新引入的 API,React 是在內存中維護了一棵 Virtual Dom Tree ,這棵樹會映射到真實的 Dom 節點,而 Portal
能夠打斷這種映射關係,它提供了一種將子節點渲染到存在於父組件之外的 DOM
節點的優秀的方案,一舉解決了漂浮層的問題,如:Dialog、Tooltip 等。markdown
ReactDOM.createPortal(child, container);
複製代碼
第一個參數(child
)是任何可渲染的 React 子元素,例如一個元素,字符串或 fragment。第二個參數(container
)是一個 DOM 元素。antd
須要注意,雖然 child 看似顯示在另外一顆 Dom 樹上,但在 Virtual Dom 中,實際上是同一棵,在下面的例子中也會看到。app
按照官網文檔,上手很是簡單dom
import React, { useState } from 'react';
import { Modal, Button } from 'antd';
const DialogPage = () => {
const [isModalVisible, setIsModalVisible] = useState(false);
const showModal = () => {
setIsModalVisible(true);
};
const handleOk = () => {
setIsModalVisible(false);
};
const handleCancel = () => {
setIsModalVisible(false);
};
return (
<> <Button type="primary" onClick={showModal}> Open Antd Modal </Button> <Modal title="Basic Modal" visible={isModalVisible} onOk={handleOk} onCancel={handleCancel} > <p>Some contents...</p> <p>Some contents...</p> <p>Some contents...</p> </Modal> </>
);
};
export default DialogPage;
複製代碼
在 index.html 中,建立一個 dialog 的容器元素:ide
<div id="root"></div>
<div id="dialog-root"></div>
複製代碼
經過組件內部狀態(visible
) 控制 Dialog 的渲染:oop
{
visible
? createPortal(
<div className="portal-sample"> {children} <Button onClick={onHide}>close</Button> </div>,
document.getElementById('dialog-root'),
)
: null;
}
複製代碼
從 React Components 能夠看出來,PortalDialog 和 DialogPage 的父子關係依然存在:post
import React, { useState } from 'react';
import { Button } from 'antd';
import PortalDialog from '@/components/PortalDialog';
const DialogPage = () => {
const [isPortalVisible, setIsPortalVisible] = useState(false);
const showPortal = () => {
setIsPortalVisible(true);
};
const hidePortal = () => {
setIsPortalVisible(false);
};
return (
<> <Button style={{ marginLeft: '20px' }} onClick={showPortal}> Open Dialog(React Portals) </Button> <PortalDialog visible={isPortalVisible} onHide={hidePortal}> <div>dialog-children</div> </PortalDialog> </>
);
};
export default DialogPage;
複製代碼
import { createPortal } from 'react-dom';
import { Button } from 'antd';
import './style.css';
const PortalDialog = (props) => {
const { visible, children, onHide } = props;
return visible
? createPortal(
<div className="portal-sample"> {children} <Button onClick={onHide}>close</Button> </div>,
document.getElementById('dialog-root'),
)
: null;
};
export default PortalDialog;
複製代碼
.portal-sample {
position: absolute;
padding: 20px;
width: 500px;
height: 300px;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: #fff;
border-radius: 10px;
border: 1px solid #ddd;
box-shadow: 0px 0px 20px 2px #ddd;
}
複製代碼