import React from 'react' type Component = React.ReactElement type ContextType = { outlet: Component | null append(id: string, component: Component): void remove(id: string): void } type ComponentMap = { [id: string]: Component } export function createPortalGroup() { const Context = React.createContext({ outlet: null, append: () => {}, remove: () => {}, }) function Provider(props: React.PropsWithChildren<{}>) { const map = React.useRef({}) const [outlet, setOutlet] = React.useState(null) const append = React.useCallback((id, component) => { if (map.current[id]) return map.current[id] = {component} setOutlet(<>{Object.values(map.current)}) }, []) const remove = React.useCallback(id => { delete map.current[id] setOutlet(<>{Object.values(map.current)}) }, []) return ( {props.children} ) } function Outlet() { const ctx = React.useContext(Context) return ctx.outlet } function Portal({children}: React.PropsWithChildren<{}>) { const {append, remove} = React.useContext(Context) const id = React.useId() React.useEffect(() => { append(id, children as Component) return () => remove(id) }, [id, children, append, remove]) return null } return {Provider, Outlet, Portal} } const DefaultPortal = createPortalGroup() export const Provider = DefaultPortal.Provider export const Outlet = DefaultPortal.Outlet export const Portal = DefaultPortal.Portal