import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; function useForceUpdate() { const [, setVal] = useState(false); return () => setVal((val) => !val); } export type PortalProps = { children: React.ReactNode; root?: string; type?: string; }; export function Portal({ children, root, type = "div" }: PortalProps) { const forceUpdate = useForceUpdate(); const mountNode = useRef(null); const portalNode = useRef(null); // oxlint-disable-next-line react-hooks/exhaustive-deps -- because reasons useEffect(() => { const ownerDocument = mountNode.current?.ownerDocument; if (ownerDocument && !portalNode.current) { let parent = root ? ownerDocument.querySelector(root) || ownerDocument.body : ownerDocument.body; const node = ownerDocument.createElement(type); portalNode.current = node; parent.appendChild(node); forceUpdate(); return () => { parent.removeChild(node); }; } }, [root, type]); if (!portalNode.current) { return
; } return createPortal(children, portalNode.current); }