Files
bsky-social-app/src/components/Portal.tsx
T
Samuel Newman 3e2c181c40 Upgrade @types/react to 19 and run codemod (attempt 2) (#8918)
* update dependencies

* rm `import type React from 'react'`

* run codemods

* patch discord types

* update types/react-dom

* Update yarn.lock
2025-09-09 08:09:10 -07:00

84 lines
1.9 KiB
TypeScript

import {
createContext,
Fragment,
useCallback,
useContext,
useEffect,
useId,
useMemo,
useRef,
useState,
} from 'react'
type Component = React.ReactElement<any>
type ContextType = {
outlet: Component | null
append(id: string, component: Component): void
remove(id: string): void
}
type ComponentMap = {
[id: string]: Component | null
}
export function createPortalGroup() {
const Context = createContext<ContextType>({
outlet: null,
append: () => {},
remove: () => {},
})
Context.displayName = 'PortalContext'
function Provider(props: React.PropsWithChildren<{}>) {
const map = useRef<ComponentMap>({})
const [outlet, setOutlet] = useState<ContextType['outlet']>(null)
const append = useCallback<ContextType['append']>((id, component) => {
if (map.current[id]) return
map.current[id] = <Fragment key={id}>{component}</Fragment>
setOutlet(<>{Object.values(map.current)}</>)
}, [])
const remove = useCallback<ContextType['remove']>(id => {
map.current[id] = null
setOutlet(<>{Object.values(map.current)}</>)
}, [])
const contextValue = useMemo(
() => ({
outlet,
append,
remove,
}),
[outlet, append, remove],
)
return (
<Context.Provider value={contextValue}>{props.children}</Context.Provider>
)
}
function Outlet() {
const ctx = useContext(Context)
return ctx.outlet
}
function Portal({children}: React.PropsWithChildren<{}>) {
const {append, remove} = useContext(Context)
const id = useId()
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