import type { ReactNode } from 'react'; import React, { memo, useLayoutEffect, useMemo } from 'react'; // Fix issue no crypto in react-native // https://stackoverflow.com/a/66852908 import { customAlphabet } from 'nanoid/non-secure'; import { usePortal } from './usePortal'; import { PortalProvider } from './PortalProvider'; import { PortalHost } from './PortalHost'; import type { Theme } from '../../theme'; import { ThemeProvider, useTheme } from '../../theme'; const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz0123456789', 10); interface PortalProps { /* * Name of the portal. If name is not provided, a random name will be generated. */ name?: string; /* * Name of the portal host. If name is not provided, a default host will be used. */ hostName?: string; /* * Content of the portal. */ children?: ReactNode | ReactNode[]; /** * Theme for portal children. */ theme?: Theme; } const PortalComponent = ({ name, hostName, children, theme }: PortalProps) => { const defaultTheme = useTheme(); const { addPortal: addUpdatePortal, removePortal } = usePortal(hostName); const nameOrRandom = useMemo(() => name || nanoid(), [name]); const ChildrenComponent = useMemo( () => ( {children} ), [theme, children, defaultTheme] ); useLayoutEffect(() => { addUpdatePortal(nameOrRandom, ChildrenComponent); return () => { removePortal(nameOrRandom); }; }, [addUpdatePortal]); useLayoutEffect(() => { addUpdatePortal(nameOrRandom, ChildrenComponent); }, [ChildrenComponent]); return null; }; const Portal = memo(PortalComponent); Portal.displayName = 'Portal'; export default Object.assign(Portal, { Provider: PortalProvider, Host: PortalHost, });