From cf0efafc4d38d903089519904bb7178f411534c4 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 4 Mar 2026 13:43:20 +0000 Subject: [PATCH] Simplify pager header measure system (#9990) --- src/view/com/pager/PagerWithHeader.tsx | 54 +++++++++++--------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/src/view/com/pager/PagerWithHeader.tsx b/src/view/com/pager/PagerWithHeader.tsx index abc6d024e5..31db71ca24 100644 --- a/src/view/com/pager/PagerWithHeader.tsx +++ b/src/view/com/pager/PagerWithHeader.tsx @@ -277,35 +277,30 @@ let PagerTabBar = ({ ], } }) - const headerRef = useRef(null) - const fallbackHeaderOnlyHeight = useRef(0) + const pendingHeaderHeightForWhenSentinelReady = useRef( + undefined, + ) + const sentinelHasRenderedRef = useRef(false) return ( { + // we want to measure this view's height to get the header height. + // however, we risk doing it too early if the header hasn't rendered yet. + // therefore, we use a sentinel view and wait for *that* to layout before + // we set the header height, using the last measured height from this + // onLayout. after the sentinel has rendered for the first time, we can + // just straightforwardly set the header height here directly -sfn const height = e.nativeEvent.layout.height - // Fallback measurement using onLayout directly on the header wrapper. - // This is more reliable than .measure() on Android after certain - // navigation transitions (e.g. returning from the logged-out view) - // where .measure() can fail to return a height. in general though, - // we should prefer using .measure() when possible as this can - // fire too early and cause layout thrashing. - // ref: https://github.com/bluesky-social/social-app/pull/9964 -sfp - if (isHeaderReady) { - fallbackHeaderOnlyHeight.current = height - // Re-measure when the header content changes size (e.g. - // SuggestedFollows accordion expanding/collapsing). The sentinel - // view below only fires onLayout once on mount, so without this - // the headerOnlyHeight goes stale. - sfp - const rounded = Math.round(height * 2) / 2 - if (rounded > 0 && rounded !== headerOnlyHeight) { - onHeaderOnlyLayout(height) - } + // note: sentinel only renders after `isHeaderReady` has turned `true` + if (sentinelHasRenderedRef.current) { + onHeaderOnlyLayout(height) + } else { + pendingHeaderHeightForWhenSentinelReady.current = height } }}> {renderHeader?.({setMinimumHeight: setMinimumHeaderHeight})} @@ -315,22 +310,19 @@ let PagerTabBar = ({ // Instead, we'll render a brand node conditionally and get fresh layout. isHeaderReady && ( { - headerRef.current?.measure( - (_x: number, _y: number, _width: number, height: number) => { - // sometimes height is `undefined` on Android, see above - if (height !== undefined) { - onHeaderOnlyLayout(height) - } else { - // if measure fails, use the value we got from `onLayout` - onHeaderOnlyLayout(fallbackHeaderOnlyHeight.current) - } - }, - ) + if (!sentinelHasRenderedRef.current) { + sentinelHasRenderedRef.current = true + const height = pendingHeaderHeightForWhenSentinelReady.current + if (height !== undefined) { + onHeaderOnlyLayout(height) + } + } }} /> )