From 4a6d4c3f983d8478c1a758d347941715f9674a14 Mon Sep 17 00:00:00 2001 From: Nathnael Dereje Date: Mon, 22 Jun 2026 12:08:36 +0200 Subject: [PATCH] fix: suppress tab-header resize oscillation that causes infinite update loop When the tabs container is near the overflow threshold, showing/hiding pagination buttons causes a layout shift (~73px) that immediately flips the overflow state back, creating an infinite ResizeObserver loop that eventually triggers React's 'Maximum update depth exceeded' error. Fix: apply a 2px threshold to the useContainerQuery callback so that sub-pixel or pagination-button-sized width changes are not propagated as new measurements, breaking the oscillation cycle. Mirrors the pattern used in useContainerWidth (PR #4615). Fixes AWSUI-62011 --- src/tabs/tab-header-bar.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tabs/tab-header-bar.tsx b/src/tabs/tab-header-bar.tsx index 1704e31172..d909115c94 100644 --- a/src/tabs/tab-header-bar.tsx +++ b/src/tabs/tab-header-bar.tsx @@ -111,7 +111,15 @@ export function TabHeaderBar({ const containerObjectRef = useRef(null); const documentRef = useRef(typeof document !== 'undefined' ? document : null); const documentRefCallback = (node: null | Element) => (documentRef.current = node?.ownerDocument ?? document); - const [widthChange, containerMeasureRef] = useContainerQuery(rect => rect.contentBoxWidth); + const [widthChange, containerMeasureRef] = useContainerQuery((rect, prev) => { + if (prev === null) { + return rect.contentBoxWidth; + } + // Suppress oscillation: pagination buttons appearing/disappearing cause a ~73px + // layout shift that would flip the overflow state again, creating an infinite loop. + // Only propagate the measurement when the change is large enough to be a genuine resize. + return Math.abs(prev - rect.contentBoxWidth) >= 2 ? rect.contentBoxWidth : prev; + }); const containerRef = useMergeRefs(containerObjectRef, containerMeasureRef, documentRefCallback); const tabRefs = useRef>(new Map()); const [horizontalOverflow, setHorizontalOverflow] = useState(false);