diff --git a/packages/portal/src/reach-portal.tsx b/packages/portal/src/reach-portal.tsx index 9811d07e6..dfac5f191 100644 --- a/packages/portal/src/reach-portal.tsx +++ b/packages/portal/src/reach-portal.tsx @@ -20,6 +20,9 @@ import { createPortal } from "react-dom"; declare const __DEV__: boolean; +const PortalContainerContext = React.createContext | null>(null); +const { Provider: PortalContainerContextProvider } = PortalContainerContext; + /** * Portal * @@ -34,6 +37,8 @@ const PortalImpl: React.FC = ({ let portalNode = React.useRef(null); let forceUpdate = useForceUpdate(); + const contextContainerRef = React.useContext(PortalContainerContext); + if (__DEV__) { // eslint-disable-next-line react-hooks/rules-of-hooks React.useEffect(() => { @@ -54,6 +59,23 @@ const PortalImpl: React.FC = ({ "See https://reactjs.org/docs/forwarding-refs.html." ); } + } else if (contextContainerRef != null) { + if (typeof contextContainerRef !== "object" || !("current" in contextContainerRef)) { + console.warn( + "@reach/portal: Invalid value passed to the `PortalContainerContext`" + + "provider. The portal will be appended to the document body, but if " + + "you want to attach it to another DOM node you must pass a valid " + + "React ref object to `containerRef`." + ); + } else if (contextContainerRef.current == null) { + console.warn( + "@reach/portal: A ref was passed to the `PortalContainerContext`" + + "provider, but no DOM node was attached to it. Be sure to pass the " + + "ref to a DOM component.\n\nIf you are forwarding the ref from " + + "another component, be sure to use the React.forwardRef API. " + + "See https://reactjs.org/docs/forwarding-refs.html." + ); + } } }, [containerRef]); } @@ -64,7 +86,7 @@ const PortalImpl: React.FC = ({ // It's possible that the content of the portal has, itself, been portaled. // In that case, it's important to append to the correct document element. let ownerDocument = mountNode.current!.ownerDocument; - let body = containerRef?.current || ownerDocument.body; + let body = containerRef?.current || contextContainerRef?.current || ownerDocument.body; portalNode.current = ownerDocument?.createElement(type)!; body.appendChild(portalNode.current); forceUpdate(); @@ -131,4 +153,4 @@ Portal.displayName = "Portal"; // Exports export type { PortalProps }; -export { Portal }; +export { Portal, PortalContainerContextProvider };