import React, { createContext, ReactNode, RefObject, useCallback, useContext, useMemo, useReducer, useRef, } from 'react'; import { StyleSheet, View } from 'react-native'; interface TooltipPortalContextValue { register: (id: string, content: ReactNode) => void; unregister: (id: string, immediate?: boolean) => void; portals: Map; hostRef: RefObject; } const TooltipPortalContext = createContext( null, ); export const TooltipPortalProvider: React.FC<{ children: ReactNode }> = ({ children, }) => { // Use ref for synchronous updates + reducer for manual re-renders const portalsRef = useRef>(new Map()); const hostRef = useRef(null); const [updateCounter, forceUpdate] = useReducer((x: number) => x + 1, 0); const pendingUpdateRef = useRef(null); const register = useCallback((id: string, content: ReactNode) => { portalsRef.current.set(id, content); forceUpdate(); // Trigger re-render to show new content }, []); const unregister = useCallback((id: string, immediate = false) => { const hasItem = portalsRef.current.has(id); if (!hasItem) return; // Already removed portalsRef.current.delete(id); // Clear any pending updates if (pendingUpdateRef.current) { clearTimeout(pendingUpdateRef.current); pendingUpdateRef.current = null; } if (immediate) { forceUpdate(); } else { // Debounce normal updates to avoid excessive re-renders pendingUpdateRef.current = setTimeout(() => { forceUpdate(); pendingUpdateRef.current = null; }, 0); } }, []); const contextValue = useMemo( () => ({ register, unregister, portals: portalsRef.current, hostRef }), // eslint-disable-next-line react-hooks/exhaustive-deps [register, unregister, updateCounter] ); return ( {children} ); }; export const TooltipPortalHost: React.FC = () => { const context = useContext(TooltipPortalContext); if (!context) { if (__DEV__) { console.warn( 'TooltipPortalHost must be used within TooltipPortalProvider', ); } return null; } const { portals, hostRef } = context; return ( {Array.from(portals.entries()).map(([id, content]) => ( {content} ))} ); }; export const useTooltipPortal = () => { const context = useContext(TooltipPortalContext); if (!context) { if (__DEV__) { console.warn( 'useTooltipPortal must be used within TooltipPortalProvider. Tooltips may not appear correctly.', ); } // Return no-op functions if context is missing return { register: () => { }, unregister: () => { }, hostRef: null, }; } return context; }; const styles = StyleSheet.create({ hostContainer: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, zIndex: 9999, }, });