perf(rn): memo and optimize list
Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
parent
6abb442f03
commit
89126ed83e
|
|
@ -1,9 +1,18 @@
|
|||
import { cn } from "@follow/utils"
|
||||
import { debounce } from "es-toolkit/compat"
|
||||
import type { FC } from "react"
|
||||
import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react"
|
||||
import {
|
||||
forwardRef,
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react"
|
||||
import type {
|
||||
Animated as AnimatedNative,
|
||||
LayoutChangeEvent,
|
||||
StyleProp,
|
||||
TouchableOpacityProps,
|
||||
ViewStyle,
|
||||
|
|
@ -115,7 +124,19 @@ export const TabBar = forwardRef<ScrollView, TabBarProps>(
|
|||
}
|
||||
}
|
||||
}, [currentTab, indicatorPosition, sharedPagerOffsetX.value, tabPositions, tabWidths])
|
||||
|
||||
const handleTabItemLayout = useCallback((event: LayoutChangeEvent, index: number) => {
|
||||
const { width, x } = event.nativeEvent.layout
|
||||
setTabWidths((prev) => {
|
||||
const newWidths = [...prev]
|
||||
newWidths[index] = width
|
||||
return newWidths
|
||||
})
|
||||
setTabPositions((prev) => {
|
||||
const newPositions = [...prev]
|
||||
newPositions[index] = x
|
||||
return newPositions
|
||||
})
|
||||
}, [])
|
||||
const indicatorStyle = useAnimatedStyle(() => {
|
||||
const scrollProgress = Math.max(sharedPagerOffsetX.value / tabBarWidth, 0)
|
||||
|
||||
|
|
@ -155,29 +176,15 @@ export const TabBar = forwardRef<ScrollView, TabBarProps>(
|
|||
style={[styles.root, tabbarStyle]}
|
||||
>
|
||||
{tabs.map((tab, index) => (
|
||||
<TabItem
|
||||
onPress={() => {
|
||||
handleChangeTabIndex(index)
|
||||
}}
|
||||
<TarBarItem
|
||||
TabItem={TabItem}
|
||||
key={tab.value}
|
||||
isSelected={index === currentTab}
|
||||
onLayout={(event) => {
|
||||
const { width, x } = event.nativeEvent.layout
|
||||
setTabWidths((prev) => {
|
||||
const newWidths = [...prev]
|
||||
newWidths[index] = width
|
||||
return newWidths
|
||||
})
|
||||
setTabPositions((prev) => {
|
||||
const newPositions = [...prev]
|
||||
newPositions[index] = x
|
||||
return newPositions
|
||||
})
|
||||
}}
|
||||
index={index}
|
||||
onTabItemPress={handleChangeTabIndex}
|
||||
onLayout={handleTabItemLayout}
|
||||
isSelected={currentTab === index}
|
||||
tab={tab}
|
||||
>
|
||||
<TabItemInner tab={tab} isSelected={index === currentTab} />
|
||||
</TabItem>
|
||||
/>
|
||||
))}
|
||||
|
||||
<Animated.View style={[styles.indicator, indicatorStyle]} />
|
||||
|
|
@ -209,3 +216,29 @@ const TabItemInner = ({ tab, isSelected }: { tab: Tab; isSelected: boolean }) =>
|
|||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const TarBarItem: FC<{
|
||||
TabItem: FC<{ isSelected: boolean; tab: Tab } & Pick<TouchableOpacityProps, "onLayout">>
|
||||
onTabItemPress: (index: number) => void
|
||||
isSelected: boolean
|
||||
tab: Tab
|
||||
|
||||
index: number
|
||||
onLayout: (event: LayoutChangeEvent, index: number) => void
|
||||
}> = memo(({ TabItem = Pressable, onTabItemPress, isSelected, tab, onLayout, index }) => {
|
||||
return (
|
||||
<TabItem
|
||||
onPress={() => {
|
||||
onTabItemPress(index)
|
||||
}}
|
||||
key={tab.value}
|
||||
isSelected={isSelected}
|
||||
onLayout={(event) => {
|
||||
onLayout(event, index)
|
||||
}}
|
||||
tab={tab}
|
||||
>
|
||||
<TabItemInner tab={tab} isSelected={isSelected} />
|
||||
</TabItem>
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { cn } from "@follow/utils"
|
||||
import type { FC } from "react"
|
||||
import { useCallback, useEffect, useRef, useState } from "react"
|
||||
import { memo, useCallback, useEffect, useRef, useState } from "react"
|
||||
import type { ScrollView, StyleProp, TouchableOpacityProps, ViewStyle } from "react-native"
|
||||
import {
|
||||
Animated as RnAnimated,
|
||||
|
|
@ -117,10 +117,30 @@ export const TabView: FC<TabViewProps> = ({
|
|||
>
|
||||
{tabs.map((tab, index) => (
|
||||
<View className="flex-1" style={{ width: windowWidth }} key={tab.value}>
|
||||
{shouldRenderCurrentTab(index) && <Tab isSelected={currentTab === index} tab={tab} />}
|
||||
{shouldRenderCurrentTab(index) && (
|
||||
<TabComponent
|
||||
key={tab.value}
|
||||
tab={tab}
|
||||
isSelected={currentTab === index}
|
||||
Tab={Tab as TabComponent}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
))}
|
||||
</AnimatedScrollView>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const TabComponent: FC<{
|
||||
tab: Tab
|
||||
isSelected: boolean
|
||||
Tab: TabComponent
|
||||
}> = memo(({ tab, isSelected, Tab = View }) => {
|
||||
const { width: windowWidth } = useWindowDimensions()
|
||||
return (
|
||||
<View className="flex-1" style={{ width: windowWidth }} key={tab.value}>
|
||||
<Tab isSelected={isSelected} tab={tab} />
|
||||
</View>
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { useHeaderHeight } from "@react-navigation/elements"
|
|||
import { FlashList } from "@shopify/flash-list"
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import type { FC } from "react"
|
||||
import { useCallback, useMemo, useRef } from "react"
|
||||
import { memo, useCallback, useMemo, useRef } from "react"
|
||||
import { Text, TouchableOpacity, View } from "react-native"
|
||||
import type { PanGestureHandlerGestureEvent } from "react-native-gesture-handler"
|
||||
import { PanGestureHandler } from "react-native-gesture-handler"
|
||||
|
|
@ -63,7 +63,7 @@ const fetchRsshubPopular = (category: DiscoverCategories, lang: Language) => {
|
|||
})
|
||||
}
|
||||
|
||||
const Tab: TabComponent = ({ tab }) => {
|
||||
const Tab: TabComponent = ({ tab, ...rest }) => {
|
||||
const tabHeight = useBottomTabBarHeight()
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
|
|
@ -150,7 +150,7 @@ const Tab: TabComponent = ({ tab }) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<View className="bg-system-background flex-1">
|
||||
<View className="bg-system-background flex-1" {...rest}>
|
||||
<FlashList
|
||||
estimatedItemSize={150}
|
||||
ref={listRef}
|
||||
|
|
@ -162,7 +162,6 @@ const Tab: TabComponent = ({ tab }) => {
|
|||
contentContainerStyle={{ paddingBottom: tabHeight }}
|
||||
removeClippedSubviews
|
||||
/>
|
||||
|
||||
{/* Right Sidebar */}
|
||||
<NavigationSidebar alphabetGroups={alphabetGroups} listRef={listRef} />
|
||||
</View>
|
||||
|
|
@ -194,7 +193,7 @@ const ItemRenderer = ({
|
|||
const NavigationSidebar: FC<{
|
||||
alphabetGroups: (string | { key: string; data: RSSHubRouteDeclaration })[]
|
||||
listRef: React.RefObject<FlashList<string | { key: string; data: RSSHubRouteDeclaration }>>
|
||||
}> = ({ alphabetGroups, listRef }) => {
|
||||
}> = memo(({ alphabetGroups, listRef }) => {
|
||||
const scrollToLetter = useCallback(
|
||||
(letter: string, animated = true) => {
|
||||
const index = alphabetGroups.findIndex((group) => {
|
||||
|
|
@ -264,4 +263,4 @@ const NavigationSidebar: FC<{
|
|||
</PanGestureHandler>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue