77 useCallback ,
88 useEffect ,
99 useMemo ,
10+ useRef ,
1011 useState ,
1112} from 'react' ;
1213import { OverlayProps } from 'react-aria' ;
@@ -393,6 +394,8 @@ export function useAutoTooltip({
393394 // Track label overflow for auto tooltip (only when enabled)
394395 const mergedLabelRef = useCombinedRefs ( ( labelProps as any ) ?. ref ) ;
395396 const [ isLabelOverflowed , setIsLabelOverflowed ] = useState ( false ) ;
397+ const observedElementRef = useRef < HTMLElement | null > ( null ) ;
398+ const resizeObserverRef = useRef < ResizeObserver | null > ( null ) ;
396399
397400 const checkLabelOverflow = useCallback ( ( ) => {
398401 const label = mergedLabelRef . current ;
@@ -402,7 +405,6 @@ export function useAutoTooltip({
402405 }
403406
404407 const hasOverflow = label . scrollWidth > label . clientWidth ;
405-
406408 setIsLabelOverflowed ( hasOverflow ) ;
407409 } , [ mergedLabelRef ] ) ;
408410
@@ -412,24 +414,61 @@ export function useAutoTooltip({
412414 }
413415 } , [ isAutoTooltipEnabled , checkLabelOverflow ] ) ;
414416
415- useEffect ( ( ) => {
416- if ( ! isAutoTooltipEnabled ) return ;
417-
418- const label = mergedLabelRef . current ;
419- if ( ! label ) return ;
417+ // Attach ResizeObserver via callback ref to handle DOM node changes
418+ const handleLabelElementRef = useCallback (
419+ ( element : HTMLElement | null ) => {
420+ // Sync to combined ref so external refs receive the node
421+ ( mergedLabelRef as any ) . current = element ;
422+
423+ // Disconnect previous observer
424+ if ( resizeObserverRef . current ) {
425+ try {
426+ resizeObserverRef . current . disconnect ( ) ;
427+ } catch {
428+ // do nothing
429+ }
430+ resizeObserverRef . current = null ;
431+ }
420432
421- const resizeObserver = new ResizeObserver ( checkLabelOverflow ) ;
422- resizeObserver . observe ( label ) ;
433+ observedElementRef . current = element ;
434+
435+ if ( element && isAutoTooltipEnabled ) {
436+ // Create a fresh observer to capture the latest callback
437+ const obs = new ResizeObserver ( ( ) => {
438+ checkLabelOverflow ( ) ;
439+ } ) ;
440+ resizeObserverRef . current = obs ;
441+ obs . observe ( element ) ;
442+ // Initial check
443+ checkLabelOverflow ( ) ;
444+ } else {
445+ setIsLabelOverflowed ( false ) ;
446+ }
447+ } ,
448+ [ mergedLabelRef , isAutoTooltipEnabled , checkLabelOverflow ] ,
449+ ) ;
423450
424- return ( ) => resizeObserver . disconnect ( ) ;
425- } , [ isAutoTooltipEnabled , checkLabelOverflow , mergedLabelRef ] ) ;
451+ // Cleanup on unmount
452+ useEffect ( ( ) => {
453+ return ( ) => {
454+ if ( resizeObserverRef . current ) {
455+ try {
456+ resizeObserverRef . current . disconnect ( ) ;
457+ } catch {
458+ // do nothing
459+ }
460+ resizeObserverRef . current = null ;
461+ }
462+ observedElementRef . current = null ;
463+ } ;
464+ } , [ ] ) ;
426465
427466 const finalLabelProps = useMemo ( ( ) => {
428467 return {
429468 ...( labelProps || { } ) ,
430- ref : mergedLabelRef ,
469+ ref : handleLabelElementRef ,
431470 } as Props & { ref ?: any } ;
432- } , [ labelProps , mergedLabelRef ] ) ;
471+ } , [ labelProps , handleLabelElementRef ] ) ;
433472
434473 const renderWithTooltip = useCallback (
435474 (
0 commit comments