1import React, {ReactNode, useEffect, useRef} from 'react'; 2import {StyleSheet, View} from 'react-native'; 3import {atom, useAtomValue, useSetAtom} from 'jotai'; 4 5interface IPortalNode { 6 key: string | null; 7 children: ReactNode; 8} 9 10const portalsAtom = atom<IPortalNode[]>([]); 11 12interface IPortalProps { 13 children: ReactNode; 14} 15export default function Portal(props: IPortalProps) { 16 const {children} = props; 17 18 const keyRef = useRef<string | null>(null); 19 const setPortalsAtoms = useSetAtom(portalsAtom); 20 21 useEffect(() => { 22 if (!keyRef.current) { 23 // mount 24 keyRef.current = Math.random().toString().slice(2); 25 // console.log("MOUNT!", keyRef.current); 26 setPortalsAtoms(portals => [ 27 ...portals, 28 {key: keyRef.current, children}, 29 ]); 30 } else { 31 // update 32 // console.log("UPDATE!", keyRef.current); 33 setPortalsAtoms(portals => 34 portals.map(it => 35 it.key === keyRef.current ? {...it, children} : it, 36 ), 37 ); 38 } 39 }, [children]); 40 41 useEffect(() => { 42 return () => { 43 if (keyRef.current) { 44 // console.log("UNMOUNT!", keyRef.current); 45 setPortalsAtoms(portals => 46 portals.filter(it => it.key !== keyRef.current), 47 ); 48 } 49 }; 50 }, []); 51 52 return null; 53} 54 55export function PortalHost() { 56 const portals = useAtomValue(portalsAtom); 57 58 return ( 59 <> 60 {portals.map(({key, children}) => ( 61 <View 62 key={key} 63 collapsable={false} 64 pointerEvents="box-none" 65 style={StyleSheet.absoluteFill}> 66 {children} 67 </View> 68 ))} 69 </> 70 ); 71} 72