1- import { useEffect , useMemo , useRef , useState } from "react" ;
1+ import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
22import type { TNavigationItem } from "./tab-navigation-root" ;
33
44export type TResponsiveTabLayout = {
55 visibleItems : TNavigationItem [ ] ;
66 overflowItems : TNavigationItem [ ] ;
77 hasOverflow : boolean ;
8- containerRef : React . RefObject < HTMLDivElement > ;
98 itemRefs : React . MutableRefObject < ( HTMLDivElement | null ) [ ] > ;
9+ containerRef : ( node : HTMLDivElement | null ) => void ;
1010} ;
1111
1212type UseResponsiveTabLayoutProps = {
@@ -30,9 +30,9 @@ export const useResponsiveTabLayout = ({
3030 hiddenNavigationItems,
3131 isActive,
3232} : UseResponsiveTabLayoutProps ) : TResponsiveTabLayout => {
33- // Refs for measuring space and items
34- const containerRef = useRef < HTMLDivElement > ( null ) ;
33+ // Refs for measuring items
3534 const itemRefs = useRef < ( HTMLDivElement | null ) [ ] > ( [ ] ) ;
35+ const resizeObserverRef = useRef < ResizeObserver | null > ( null ) ;
3636
3737 // State for responsive behavior
3838 const [ containerWidth , setContainerWidth ] = useState < number > ( 0 ) ;
@@ -42,24 +42,44 @@ export const useResponsiveTabLayout = ({
4242 const gap = 4 ; // gap-1 = 4px
4343 const overflowButtonWidth = 40 ;
4444
45- const container = containerRef ?. current ;
45+ // Callback ref that sets up ResizeObserver when element is attached
46+ const containerRef = useCallback ( ( node : HTMLDivElement | null ) => {
47+ // Clean up previous observer if it exists
48+ if ( resizeObserverRef . current ) {
49+ resizeObserverRef . current . disconnect ( ) ;
50+ resizeObserverRef . current = null ;
51+ }
4652
47- // ResizeObserver to measure container width
48- useEffect ( ( ) => {
49- if ( ! container ) return ;
53+ // If node is null (unmounting), just clean up
54+ if ( ! node ) {
55+ setContainerWidth ( 0 ) ;
56+ return ;
57+ }
5058
59+ // Set initial width immediately
60+ setContainerWidth ( node . offsetWidth ) ;
61+
62+ // Create and set up new ResizeObserver
5163 const resizeObserver = new ResizeObserver ( ( entries ) => {
5264 for ( const entry of entries ) {
5365 setContainerWidth ( entry . contentRect . width ) ;
5466 }
5567 } ) ;
5668
57- resizeObserver . observe ( container ) ;
69+ resizeObserverRef . current = resizeObserver ;
70+ resizeObserver . observe ( node ) ;
71+ } , [ ] ) ; // Empty deps - callback function remains stable
5872
59- return ( ) => {
60- resizeObserver . disconnect ( ) ;
61- } ;
62- } , [ container ] ) ;
73+ // Cleanup effect to disconnect observer on component unmount
74+ useEffect (
75+ ( ) => ( ) => {
76+ if ( resizeObserverRef . current ) {
77+ resizeObserverRef . current . disconnect ( ) ;
78+ resizeObserverRef . current = null ;
79+ }
80+ } ,
81+ [ ]
82+ ) ;
6383
6484 // Calculate how many items can fit
6585 useEffect ( ( ) => {
@@ -137,7 +157,7 @@ export const useResponsiveTabLayout = ({
137157 visibleItems,
138158 overflowItems,
139159 hasOverflow,
140- containerRef,
141160 itemRefs,
161+ containerRef,
142162 } ;
143163} ;
0 commit comments