[ReactDoc] Portals
- 內文資料來源:Portals @ React Docs
目的
透過 Portal,可以在 React root DOM node(一般是 #app
) 以外的地方 DOM node 將 React 的內容 render 出來。同時,也能以解決 z-index 和 stacking context 導致 UI 被其他元素覆蓋的問題。
範例
HTML
雖然這裡建立了兩個 DOM,但透過 React.createPortal
可以把 #root
的 React children 在 #modal-root
中 render 出來;此外,雖然從 HTML 來看,#root
和 #modal-root
是 sibling 的關係,但透過 React.createPortal
,在 #modal-root
內所做的操作和行為(例如,event bubbling),就會像是在 #root
裡面一樣。
<!-- index.html -->
<body>
<div id="root"></div>
<div id="modal-root"></div>
</body>
React Component
Modal
ReactDOM.createPortal()
的 API 和 React 17 以前的 ReactDOM.render()
很像,第一個參數放的是 JSX,第二個參數則是要被 mounted 上去的 DOM 元素。
const Modal = ({ children }) => {
// STEP 1: 建立一個 div
const el = useRef(document.createElement('div'));
useEffect(() => {
// STEP 2: 找到 #modal-root
const modalRoot = document.getElementById('modal-root');
// STEP 3: 把 div 放到 #modal-root
modalRoot.appendChild(el.current);
return () => {
// STEP 5: 將 #modal-root 中的 div 移除
modalRoot.removeChild(el.current);
};
}, []);
console.log(el.current);
// STEP 4: 將 children 的內容放到 div 中
return ReactDOM.createPortal(children, el.current);
};
App
- 在
<Modal />
中 children 的內容,會出現在#modal-root
中
const DemoPortal = () => {
const [showModal, setShowModal] = useState(false);
const handleShow = () => {
setShowModal(true);
};
const handleHide = () => {
setShowModal(false);
};
return (
<div>
<button onClick={handleShow}>Show Modal</button>
{showModal && (
<Modal>
<div className="modal">
<div>
With a portal, we can render content into a different part of the DOM, as if it were
any other React child.
</div>
This is being rendered inside the #modal-container div.
<button onClick={handleHide}>Hide modal</button>
</div>
</Modal>
)}
</div>
);
};
Material UI Modal 的實作
Material UI Modal 的實作可以參考這幾隻檔案:
Material UI Modal 預設會使用 React.createPortal()
並把它 mount 在 document.body
上(可以從這裡的程式碼看到)。