import { ReactNode, useCallback, useEffect, useMemo, useRef } from 'react'; import { createPortal } from 'react-dom'; type UsePortalOptionsType = { bindTo?: HTMLElement; isOpen?: boolean; }; type UsePortalType = { Portal: ({ children }: { children: ReactNode }) => React.ReactPortal | null; } export function usePortal({ bindTo }: UsePortalOptionsType = {}): UsePortalType { const portal = useRef(document.createElement('div')); const bindToElement = useMemo(() => (bindTo) || document.body, [ bindTo ]); useEffect(() => { if (!portal.current) { portal.current = document.createElement('div'); } }, [ portal ]); useEffect(() => { if (!(bindToElement instanceof HTMLElement) || !(portal.current instanceof HTMLElement)) { return; } bindToElement.appendChild(portal.current); return () => { bindToElement.removeChild(portal.current); }; }, [ bindToElement, portal ]); const Portal = useCallback(({ children }: { children: ReactNode }) => { if (portal.current) { return createPortal(children, portal.current); } return null; }, [ portal ]); // @ts-ignore return { Portal }; }