about summary refs log tree commit diff
path: root/src/view/com/pager
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/pager')
-rw-r--r--src/view/com/pager/Pager.tsx220
-rw-r--r--src/view/com/pager/Pager.web.tsx47
-rw-r--r--src/view/com/pager/PagerWithHeader.tsx315
3 files changed, 299 insertions, 283 deletions
diff --git a/src/view/com/pager/Pager.tsx b/src/view/com/pager/Pager.tsx
index f62bffc53..8cc346903 100644
--- a/src/view/com/pager/Pager.tsx
+++ b/src/view/com/pager/Pager.tsx
@@ -1,16 +1,22 @@
-import React, {forwardRef, useCallback, useContext} from 'react'
+import {
+  useCallback,
+  useContext,
+  useImperativeHandle,
+  useRef,
+  useState,
+} from 'react'
 import {View} from 'react-native'
 import {DrawerGestureContext} from 'react-native-drawer-layout'
 import {Gesture, GestureDetector} from 'react-native-gesture-handler'
 import PagerView, {
-  PagerViewOnPageScrollEventData,
-  PagerViewOnPageSelectedEvent,
-  PagerViewOnPageSelectedEventData,
-  PageScrollStateChangedNativeEventData,
+  type PagerViewOnPageScrollEventData,
+  type PagerViewOnPageSelectedEvent,
+  type PagerViewOnPageSelectedEventData,
+  type PageScrollStateChangedNativeEventData,
 } from 'react-native-pager-view'
 import Animated, {
   runOnJS,
-  SharedValue,
+  type SharedValue,
   useEvent,
   useHandler,
   useSharedValue,
@@ -36,8 +42,12 @@ export interface RenderTabBarFnProps {
 export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element
 
 interface Props {
+  ref?: React.Ref<PagerRef>
   initialPage?: number
   renderTabBar: RenderTabBarFn
+  // tab pressed, yet to scroll to page
+  onTabPressed?: (index: number) => void
+  // scroll settled
   onPageSelected?: (index: number) => void
   onPageScrollStateChanged?: (
     scrollState: 'idle' | 'dragging' | 'settling',
@@ -47,114 +57,112 @@ interface Props {
 
 const AnimatedPagerView = Animated.createAnimatedComponent(PagerView)
 
-export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>(
-  function PagerImpl(
-    {
-      children,
-      initialPage = 0,
-      renderTabBar,
-      onPageScrollStateChanged: parentOnPageScrollStateChanged,
-      onPageSelected: parentOnPageSelected,
-      testID,
-    }: React.PropsWithChildren<Props>,
-    ref,
-  ) {
-    const [selectedPage, setSelectedPage] = React.useState(initialPage)
-    const pagerView = React.useRef<PagerView>(null)
+export function Pager({
+  ref,
+  children,
+  initialPage = 0,
+  renderTabBar,
+  onPageSelected: parentOnPageSelected,
+  onTabPressed: parentOnTabPressed,
+  onPageScrollStateChanged: parentOnPageScrollStateChanged,
+  testID,
+}: React.PropsWithChildren<Props>) {
+  const [selectedPage, setSelectedPage] = useState(initialPage)
+  const pagerView = useRef<PagerView>(null)
 
-    const [isIdle, setIsIdle] = React.useState(true)
-    const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
-    useFocusEffect(
-      useCallback(() => {
-        const canSwipeDrawer = selectedPage === 0 && isIdle
-        setDrawerSwipeDisabled(!canSwipeDrawer)
-        return () => {
-          setDrawerSwipeDisabled(false)
-        }
-      }, [setDrawerSwipeDisabled, selectedPage, isIdle]),
-    )
+  const [isIdle, setIsIdle] = useState(true)
+  const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
+  useFocusEffect(
+    useCallback(() => {
+      const canSwipeDrawer = selectedPage === 0 && isIdle
+      setDrawerSwipeDisabled(!canSwipeDrawer)
+      return () => {
+        setDrawerSwipeDisabled(false)
+      }
+    }, [setDrawerSwipeDisabled, selectedPage, isIdle]),
+  )
 
-    React.useImperativeHandle(ref, () => ({
-      setPage: (index: number) => {
-        pagerView.current?.setPage(index)
-      },
-    }))
+  useImperativeHandle(ref, () => ({
+    setPage: (index: number) => {
+      pagerView.current?.setPage(index)
+    },
+  }))
 
-    const onPageSelectedJSThread = React.useCallback(
-      (nextPosition: number) => {
-        setSelectedPage(nextPosition)
-        parentOnPageSelected?.(nextPosition)
-      },
-      [setSelectedPage, parentOnPageSelected],
-    )
+  const onPageSelectedJSThread = useCallback(
+    (nextPosition: number) => {
+      setSelectedPage(nextPosition)
+      parentOnPageSelected?.(nextPosition)
+    },
+    [setSelectedPage, parentOnPageSelected],
+  )
 
-    const onTabBarSelect = React.useCallback(
-      (index: number) => {
-        pagerView.current?.setPage(index)
-      },
-      [pagerView],
-    )
+  const onTabBarSelect = useCallback(
+    (index: number) => {
+      parentOnTabPressed?.(index)
+      pagerView.current?.setPage(index)
+    },
+    [pagerView, parentOnTabPressed],
+  )
 
-    const dragState = useSharedValue<'idle' | 'settling' | 'dragging'>('idle')
-    const dragProgress = useSharedValue(selectedPage)
-    const didInit = useSharedValue(false)
-    const handlePageScroll = usePagerHandlers(
-      {
-        onPageScroll(e: PagerViewOnPageScrollEventData) {
-          'worklet'
-          if (didInit.get() === false) {
-            // On iOS, there's a spurious scroll event with 0 position
-            // even if a different page was supplied as the initial page.
-            // Ignore it and wait for the first confirmed selection instead.
-            return
-          }
-          dragProgress.set(e.offset + e.position)
-        },
-        onPageScrollStateChanged(e: PageScrollStateChangedNativeEventData) {
-          'worklet'
-          runOnJS(setIsIdle)(e.pageScrollState === 'idle')
-          if (dragState.get() === 'idle' && e.pageScrollState === 'settling') {
-            // This is a programmatic scroll on Android.
-            // Stay "idle" to match iOS and avoid confusing downstream code.
-            return
-          }
-          dragState.set(e.pageScrollState)
-          parentOnPageScrollStateChanged?.(e.pageScrollState)
-        },
-        onPageSelected(e: PagerViewOnPageSelectedEventData) {
-          'worklet'
-          didInit.set(true)
-          runOnJS(onPageSelectedJSThread)(e.position)
-        },
+  const dragState = useSharedValue<'idle' | 'settling' | 'dragging'>('idle')
+  const dragProgress = useSharedValue(selectedPage)
+  const didInit = useSharedValue(false)
+  const handlePageScroll = usePagerHandlers(
+    {
+      onPageScroll(e: PagerViewOnPageScrollEventData) {
+        'worklet'
+        if (didInit.get() === false) {
+          // On iOS, there's a spurious scroll event with 0 position
+          // even if a different page was supplied as the initial page.
+          // Ignore it and wait for the first confirmed selection instead.
+          return
+        }
+        dragProgress.set(e.offset + e.position)
+      },
+      onPageScrollStateChanged(e: PageScrollStateChangedNativeEventData) {
+        'worklet'
+        runOnJS(setIsIdle)(e.pageScrollState === 'idle')
+        if (dragState.get() === 'idle' && e.pageScrollState === 'settling') {
+          // This is a programmatic scroll on Android.
+          // Stay "idle" to match iOS and avoid confusing downstream code.
+          return
+        }
+        dragState.set(e.pageScrollState)
+        parentOnPageScrollStateChanged?.(e.pageScrollState)
+      },
+      onPageSelected(e: PagerViewOnPageSelectedEventData) {
+        'worklet'
+        didInit.set(true)
+        runOnJS(onPageSelectedJSThread)(e.position)
       },
-      [parentOnPageScrollStateChanged],
-    )
+    },
+    [parentOnPageScrollStateChanged],
+  )
 
-    const drawerGesture = useContext(DrawerGestureContext) ?? Gesture.Native() // noop for web
-    const nativeGesture =
-      Gesture.Native().requireExternalGestureToFail(drawerGesture)
+  const drawerGesture = useContext(DrawerGestureContext) ?? Gesture.Native() // noop for web
+  const nativeGesture =
+    Gesture.Native().requireExternalGestureToFail(drawerGesture)
 
-    return (
-      <View testID={testID} style={[a.flex_1, native(a.overflow_hidden)]}>
-        {renderTabBar({
-          selectedPage,
-          onSelect: onTabBarSelect,
-          dragProgress,
-          dragState,
-        })}
-        <GestureDetector gesture={nativeGesture}>
-          <AnimatedPagerView
-            ref={pagerView}
-            style={[a.flex_1]}
-            initialPage={initialPage}
-            onPageScroll={handlePageScroll}>
-            {children}
-          </AnimatedPagerView>
-        </GestureDetector>
-      </View>
-    )
-  },
-)
+  return (
+    <View testID={testID} style={[a.flex_1, native(a.overflow_hidden)]}>
+      {renderTabBar({
+        selectedPage,
+        onSelect: onTabBarSelect,
+        dragProgress,
+        dragState,
+      })}
+      <GestureDetector gesture={nativeGesture}>
+        <AnimatedPagerView
+          ref={pagerView}
+          style={[a.flex_1]}
+          initialPage={initialPage}
+          onPageScroll={handlePageScroll}>
+          {children}
+        </AnimatedPagerView>
+      </GestureDetector>
+    </View>
+  )
+}
 
 function usePagerHandlers(
   handlers: {
diff --git a/src/view/com/pager/Pager.web.tsx b/src/view/com/pager/Pager.web.tsx
index c620e73e3..06aac169c 100644
--- a/src/view/com/pager/Pager.web.tsx
+++ b/src/view/com/pager/Pager.web.tsx
@@ -1,8 +1,19 @@
-import React from 'react'
+import {
+  Children,
+  useCallback,
+  useImperativeHandle,
+  useRef,
+  useState,
+} from 'react'
 import {View} from 'react-native'
 import {flushSync} from 'react-dom'
 
 import {s} from '#/lib/styles'
+import {atoms as a} from '#/alf'
+
+export interface PagerRef {
+  setPage: (index: number) => void
+}
 
 export interface RenderTabBarFnProps {
   selectedPage: number
@@ -12,30 +23,30 @@ export interface RenderTabBarFnProps {
 export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element
 
 interface Props {
+  ref?: React.Ref<PagerRef>
   initialPage?: number
   renderTabBar: RenderTabBarFn
   onPageSelected?: (index: number) => void
 }
-export const Pager = React.forwardRef(function PagerImpl(
-  {
-    children,
-    initialPage = 0,
-    renderTabBar,
-    onPageSelected,
-  }: React.PropsWithChildren<Props>,
+
+export function Pager({
   ref,
-) {
-  const [selectedPage, setSelectedPage] = React.useState(initialPage)
-  const scrollYs = React.useRef<Array<number | null>>([])
-  const anchorRef = React.useRef(null)
+  children,
+  initialPage = 0,
+  renderTabBar,
+  onPageSelected,
+}: React.PropsWithChildren<Props>) {
+  const [selectedPage, setSelectedPage] = useState(initialPage)
+  const scrollYs = useRef<Array<number | null>>([])
+  const anchorRef = useRef(null)
 
-  React.useImperativeHandle(ref, () => ({
+  useImperativeHandle(ref, () => ({
     setPage: (index: number) => {
       onTabBarSelect(index)
     },
   }))
 
-  const onTabBarSelect = React.useCallback(
+  const onTabBarSelect = useCallback(
     (index: number) => {
       const scrollY = window.scrollY
       // We want to determine if the tabbar is already "sticking" at the top (in which
@@ -75,11 +86,13 @@ export const Pager = React.forwardRef(function PagerImpl(
         tabBarAnchor: <View ref={anchorRef} />,
         onSelect: e => onTabBarSelect(e),
       })}
-      {React.Children.map(children, (child, i) => (
-        <View style={selectedPage === i ? s.flex1 : s.hidden} key={`page-${i}`}>
+      {Children.map(children, (child, i) => (
+        <View
+          style={selectedPage === i ? a.flex_1 : a.hidden}
+          key={`page-${i}`}>
           {child}
         </View>
       ))}
     </View>
   )
-})
+}
diff --git a/src/view/com/pager/PagerWithHeader.tsx b/src/view/com/pager/PagerWithHeader.tsx
index 1746d2ca1..57aaac074 100644
--- a/src/view/com/pager/PagerWithHeader.tsx
+++ b/src/view/com/pager/PagerWithHeader.tsx
@@ -1,17 +1,16 @@
-import * as React from 'react'
+import {memo, useCallback, useEffect, useRef, useState} from 'react'
 import {
-  LayoutChangeEvent,
-  NativeScrollEvent,
-  ScrollView,
+  type LayoutChangeEvent,
+  type NativeScrollEvent,
+  type ScrollView,
   StyleSheet,
   View,
 } from 'react-native'
 import Animated, {
-  AnimatedRef,
-  runOnJS,
+  type AnimatedRef,
   runOnUI,
   scrollTo,
-  SharedValue,
+  type SharedValue,
   useAnimatedRef,
   useAnimatedStyle,
   useSharedValue,
@@ -20,9 +19,13 @@ import Animated, {
 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
 import {ScrollProvider} from '#/lib/ScrollContext'
 import {isIOS} from '#/platform/detection'
-import {Pager, PagerRef, RenderTabBarFnProps} from '#/view/com/pager/Pager'
+import {
+  Pager,
+  type PagerRef,
+  type RenderTabBarFnProps,
+} from '#/view/com/pager/Pager'
 import {useTheme} from '#/alf'
-import {ListMethods} from '../util/List'
+import {type ListMethods} from '../util/List'
 import {PagerHeaderProvider} from './PagerHeaderContext'
 import {TabBar} from './TabBar'
 
@@ -33,6 +36,7 @@ export interface PagerWithHeaderChildParams {
 }
 
 export interface PagerWithHeaderProps {
+  ref?: React.Ref<PagerRef>
   testID?: string
   children:
     | (((props: PagerWithHeaderChildParams) => JSX.Element) | null)[]
@@ -49,97 +53,94 @@ export interface PagerWithHeaderProps {
   onCurrentPageSelected?: (index: number) => void
   allowHeaderOverScroll?: boolean
 }
-export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
-  function PageWithHeaderImpl(
-    {
-      children,
-      testID,
+export function PagerWithHeader({
+  ref,
+  children,
+  testID,
+  items,
+  isHeaderReady,
+  renderHeader,
+  initialPage,
+  onPageSelected,
+  onCurrentPageSelected,
+  allowHeaderOverScroll,
+}: PagerWithHeaderProps) {
+  const [currentPage, setCurrentPage] = useState(0)
+  const [tabBarHeight, setTabBarHeight] = useState(0)
+  const [headerOnlyHeight, setHeaderOnlyHeight] = useState(0)
+  const scrollY = useSharedValue(0)
+  const headerHeight = headerOnlyHeight + tabBarHeight
+
+  // capture the header bar sizing
+  const onTabBarLayout = useNonReactiveCallback((evt: LayoutChangeEvent) => {
+    const height = evt.nativeEvent.layout.height
+    if (height > 0) {
+      // The rounding is necessary to prevent jumps on iOS
+      setTabBarHeight(Math.round(height * 2) / 2)
+    }
+  })
+  const onHeaderOnlyLayout = useNonReactiveCallback((height: number) => {
+    if (height > 0) {
+      // The rounding is necessary to prevent jumps on iOS
+      setHeaderOnlyHeight(Math.round(height * 2) / 2)
+    }
+  })
+
+  const renderTabBar = useCallback(
+    (props: RenderTabBarFnProps) => {
+      return (
+        <PagerHeaderProvider scrollY={scrollY} headerHeight={headerOnlyHeight}>
+          <PagerTabBar
+            headerOnlyHeight={headerOnlyHeight}
+            items={items}
+            isHeaderReady={isHeaderReady}
+            renderHeader={renderHeader}
+            currentPage={currentPage}
+            onCurrentPageSelected={onCurrentPageSelected}
+            onTabBarLayout={onTabBarLayout}
+            onHeaderOnlyLayout={onHeaderOnlyLayout}
+            onSelect={props.onSelect}
+            scrollY={scrollY}
+            testID={testID}
+            allowHeaderOverScroll={allowHeaderOverScroll}
+            dragProgress={props.dragProgress}
+            dragState={props.dragState}
+          />
+        </PagerHeaderProvider>
+      )
+    },
+    [
+      headerOnlyHeight,
       items,
       isHeaderReady,
       renderHeader,
-      initialPage,
-      onPageSelected,
+      currentPage,
       onCurrentPageSelected,
+      onTabBarLayout,
+      onHeaderOnlyLayout,
+      scrollY,
+      testID,
       allowHeaderOverScroll,
-    }: PagerWithHeaderProps,
-    ref,
-  ) {
-    const [currentPage, setCurrentPage] = React.useState(0)
-    const [tabBarHeight, setTabBarHeight] = React.useState(0)
-    const [headerOnlyHeight, setHeaderOnlyHeight] = React.useState(0)
-    const scrollY = useSharedValue(0)
-    const headerHeight = headerOnlyHeight + tabBarHeight
-
-    // capture the header bar sizing
-    const onTabBarLayout = useNonReactiveCallback((evt: LayoutChangeEvent) => {
-      const height = evt.nativeEvent.layout.height
-      if (height > 0) {
-        // The rounding is necessary to prevent jumps on iOS
-        setTabBarHeight(Math.round(height * 2) / 2)
-      }
-    })
-    const onHeaderOnlyLayout = useNonReactiveCallback((height: number) => {
-      if (height > 0) {
-        // The rounding is necessary to prevent jumps on iOS
-        setHeaderOnlyHeight(Math.round(height * 2) / 2)
-      }
-    })
-
-    const renderTabBar = React.useCallback(
-      (props: RenderTabBarFnProps) => {
-        return (
-          <PagerHeaderProvider
-            scrollY={scrollY}
-            headerHeight={headerOnlyHeight}>
-            <PagerTabBar
-              headerOnlyHeight={headerOnlyHeight}
-              items={items}
-              isHeaderReady={isHeaderReady}
-              renderHeader={renderHeader}
-              currentPage={currentPage}
-              onCurrentPageSelected={onCurrentPageSelected}
-              onTabBarLayout={onTabBarLayout}
-              onHeaderOnlyLayout={onHeaderOnlyLayout}
-              onSelect={props.onSelect}
-              scrollY={scrollY}
-              testID={testID}
-              allowHeaderOverScroll={allowHeaderOverScroll}
-              dragProgress={props.dragProgress}
-              dragState={props.dragState}
-            />
-          </PagerHeaderProvider>
-        )
-      },
-      [
-        headerOnlyHeight,
-        items,
-        isHeaderReady,
-        renderHeader,
-        currentPage,
-        onCurrentPageSelected,
-        onTabBarLayout,
-        onHeaderOnlyLayout,
-        scrollY,
-        testID,
-        allowHeaderOverScroll,
-      ],
-    )
+    ],
+  )
 
-    const scrollRefs = useSharedValue<Array<AnimatedRef<any> | null>>([])
-    const registerRef = React.useCallback(
-      (scrollRef: AnimatedRef<any> | null, atIndex: number) => {
-        scrollRefs.modify(refs => {
-          'worklet'
-          refs[atIndex] = scrollRef
-          return refs
-        })
-      },
-      [scrollRefs],
-    )
+  const scrollRefs = useSharedValue<Array<AnimatedRef<any> | null>>([])
+  const registerRef = useCallback(
+    (scrollRef: AnimatedRef<any> | null, atIndex: number) => {
+      scrollRefs.modify(refs => {
+        'worklet'
+        refs[atIndex] = scrollRef
+        return refs
+      })
+    },
+    [scrollRefs],
+  )
 
-    const lastForcedScrollY = useSharedValue(0)
-    const adjustScrollForOtherPages = () => {
+  const lastForcedScrollY = useSharedValue(0)
+  const adjustScrollForOtherPages = useCallback(
+    (scrollState: 'idle' | 'dragging' | 'settling') => {
       'worklet'
+      if (scrollState !== 'dragging') return
       const currentScrollY = scrollY.get()
       const forcedScrollY = Math.min(currentScrollY, headerOnlyHeight)
       if (lastForcedScrollY.get() !== forcedScrollY) {
@@ -152,75 +153,69 @@ export const PagerWithHeader = React.forwardRef<PagerRef, PagerWithHeaderProps>(
           }
         }
       }
-    }
+    },
+    [currentPage, headerOnlyHeight, lastForcedScrollY, scrollRefs, scrollY],
+  )
 
-    const throttleTimeout = React.useRef<ReturnType<typeof setTimeout> | null>(
-      null,
-    )
-    const queueThrottledOnScroll = useNonReactiveCallback(() => {
-      if (!throttleTimeout.current) {
-        throttleTimeout.current = setTimeout(() => {
-          throttleTimeout.current = null
-          runOnUI(adjustScrollForOtherPages)()
-        }, 80 /* Sync often enough you're unlikely to catch it unsynced */)
+  const onScrollWorklet = useCallback(
+    (e: NativeScrollEvent) => {
+      'worklet'
+      const nextScrollY = e.contentOffset.y
+      // HACK: onScroll is reporting some strange values on load (negative header height).
+      // Highly improbable that you'd be overscrolled by over 400px -
+      // in fact, I actually can't do it, so let's just ignore those. -sfn
+      const isPossiblyInvalid =
+        headerHeight > 0 && Math.round(nextScrollY * 2) / 2 === -headerHeight
+      if (!isPossiblyInvalid) {
+        scrollY.set(nextScrollY)
       }
-    })
+    },
+    [scrollY, headerHeight],
+  )
 
-    const onScrollWorklet = React.useCallback(
-      (e: NativeScrollEvent) => {
-        'worklet'
-        const nextScrollY = e.contentOffset.y
-        // HACK: onScroll is reporting some strange values on load (negative header height).
-        // Highly improbable that you'd be overscrolled by over 400px -
-        // in fact, I actually can't do it, so let's just ignore those. -sfn
-        const isPossiblyInvalid =
-          headerHeight > 0 && Math.round(nextScrollY * 2) / 2 === -headerHeight
-        if (!isPossiblyInvalid) {
-          scrollY.set(nextScrollY)
-          runOnJS(queueThrottledOnScroll)()
-        }
-      },
-      [scrollY, queueThrottledOnScroll, headerHeight],
-    )
+  const onPageSelectedInner = useCallback(
+    (index: number) => {
+      setCurrentPage(index)
+      onPageSelected?.(index)
+    },
+    [onPageSelected, setCurrentPage],
+  )
 
-    const onPageSelectedInner = React.useCallback(
-      (index: number) => {
-        setCurrentPage(index)
-        onPageSelected?.(index)
-      },
-      [onPageSelected, setCurrentPage],
-    )
+  const onTabPressed = useCallback(() => {
+    runOnUI(adjustScrollForOtherPages)('dragging')
+  }, [adjustScrollForOtherPages])
 
-    return (
-      <Pager
-        ref={ref}
-        testID={testID}
-        initialPage={initialPage}
-        onPageSelected={onPageSelectedInner}
-        renderTabBar={renderTabBar}>
-        {toArray(children)
-          .filter(Boolean)
-          .map((child, i) => {
-            const isReady =
-              isHeaderReady && headerOnlyHeight > 0 && tabBarHeight > 0
-            return (
-              <View key={i} collapsable={false}>
-                <PagerItem
-                  headerHeight={headerHeight}
-                  index={i}
-                  isReady={isReady}
-                  isFocused={i === currentPage}
-                  onScrollWorklet={i === currentPage ? onScrollWorklet : noop}
-                  registerRef={registerRef}
-                  renderTab={child}
-                />
-              </View>
-            )
-          })}
-      </Pager>
-    )
-  },
-)
+  return (
+    <Pager
+      ref={ref}
+      testID={testID}
+      initialPage={initialPage}
+      onTabPressed={onTabPressed}
+      onPageSelected={onPageSelectedInner}
+      renderTabBar={renderTabBar}
+      onPageScrollStateChanged={adjustScrollForOtherPages}>
+      {toArray(children)
+        .filter(Boolean)
+        .map((child, i) => {
+          const isReady =
+            isHeaderReady && headerOnlyHeight > 0 && tabBarHeight > 0
+          return (
+            <View key={i} collapsable={false}>
+              <PagerItem
+                headerHeight={headerHeight}
+                index={i}
+                isReady={isReady}
+                isFocused={i === currentPage}
+                onScrollWorklet={i === currentPage ? onScrollWorklet : noop}
+                registerRef={registerRef}
+                renderTab={child}
+              />
+            </View>
+          )
+        })}
+    </Pager>
+  )
+}
 
 let PagerTabBar = ({
   currentPage,
@@ -258,7 +253,7 @@ let PagerTabBar = ({
   dragState: SharedValue<'idle' | 'dragging' | 'settling'>
 }): React.ReactNode => {
   const t = useTheme()
-  const [minimumHeaderHeight, setMinimumHeaderHeight] = React.useState(0)
+  const [minimumHeaderHeight, setMinimumHeaderHeight] = useState(0)
   const headerTransform = useAnimatedStyle(() => {
     const translateY =
       Math.min(
@@ -275,7 +270,7 @@ let PagerTabBar = ({
       ],
     }
   })
-  const headerRef = React.useRef(null)
+  const headerRef = useRef(null)
   return (
     <Animated.View
       pointerEvents={isIOS ? 'auto' : 'box-none'}
@@ -327,7 +322,7 @@ let PagerTabBar = ({
     </Animated.View>
   )
 }
-PagerTabBar = React.memo(PagerTabBar)
+PagerTabBar = memo(PagerTabBar)
 
 function PagerItem({
   headerHeight,
@@ -348,7 +343,7 @@ function PagerItem({
 }) {
   const scrollElRef = useAnimatedRef()
 
-  React.useEffect(() => {
+  useEffect(() => {
     registerRef(scrollElRef, index)
     return () => {
       registerRef(null, index)