feat: 使用 useIsFocused hook 优化组件渲染,页面失焦时不渲染列表项以减少内存占用

This commit is contained in:
康猛 2026-01-19 15:38:29 +08:00
parent 912c380a8a
commit ef4114f694
4 changed files with 39 additions and 9 deletions

View File

@ -1,4 +1,5 @@
import { FontAwesome, Fontisto, Ionicons } from '@expo/vector-icons'
import { useIsFocused } from '@react-navigation/native'
import { FlashList } from '@shopify/flash-list'
import { useFocusEffect, useRouter } from 'expo-router'
import { observer } from 'mobx-react-lite'
@ -42,8 +43,11 @@ const Generate = observer(function Generate() {
const templates = useTemplates()
const { runTemplate } = useTemplateActions()
// 直接使用 useIsFocused hook页面失焦时不渲染列表项以减少内存占用
const isFocused = useIsFocused()
useFocusEffect(() => {
if (!isAuthenticated) {
if (!isAuthenticated && isFocused) {
router.replace('/')
router.push('/auth')
}
@ -253,11 +257,13 @@ const Generate = observer(function Generate() {
item={item}
key={item?.id}
itemWidth={itemWidth}
// 页面失焦时不渲染,减少内存占用
isVisible={isFocused}
onSelect={() => handleSelectTemplate(item)}
/>
)
},
[selectedTemplateId, itemWidth, handleSelectTemplate],
[selectedTemplateId, itemWidth, handleSelectTemplate, isFocused],
)
const ListHeader = useMemo(() => {
@ -457,9 +463,16 @@ type TemplateItemProps = {
item: Template
itemWidth: number
isSelected: boolean
isVisible?: boolean
onSelect: () => void
}
const TemplateItem = memo<TemplateItemProps>(function TemplateItem({ item, itemWidth, isSelected, onSelect }) {
const TemplateItem = memo<TemplateItemProps>(function TemplateItem({
item,
itemWidth,
isSelected,
isVisible = true,
onSelect,
}) {
return (
<Block
key={item?.id}
@ -475,7 +488,7 @@ const TemplateItem = memo<TemplateItemProps>(function TemplateItem({ item, itemW
onClick={onSelect}
>
{/* <Img className="size-full" contentFit="cover" src={item.image} /> */}
<VideoBox style={{ width: itemWidth, height: itemWidth }} url={item.videoUrl} />
{isVisible && <VideoBox style={{ width: itemWidth, height: itemWidth }} url={item.videoUrl} />}
{isSelected && <Block className="absolute inset-0 bg-black/60" />}
{isSelected && <Block className="absolute inset-[-5px] border-[3px] border-accent" />}

View File

@ -1,4 +1,5 @@
import { Ionicons } from '@expo/vector-icons'
import { useIsFocused } from '@react-navigation/native'
import { Block, ConfirmModal, Img, ListEmpty, Text, Toast, VideoBox } from '@share/components'
import { FlashList } from '@shopify/flash-list'
import { LinearGradient } from 'expo-linear-gradient'
@ -54,6 +55,9 @@ const Index = observer(function Index() {
const [allItems, setAllItems] = useState<MediaItem[]>([])
const [selectedItem, setSelectedItem] = useState<MediaItem | null>(null)
// 直接使用 useIsFocused hook无需手动管理状态
const isFocused = useIsFocused()
const [refreshing, setRefreshing] = useState(false)
const [loadingMore, setLoadingMore] = useState(false)
const [hasMore, setHasMore] = useState(true)
@ -391,11 +395,12 @@ const Index = observer(function Index() {
isSelected={selectedId === item.id}
item={item}
itemWidth={ITEM_WIDTH}
isVisible={index < 9 || visibleIdsRef.current.has(item.id)}
// 页面失焦时不渲染,减少内存占用
isVisible={isFocused && (index < 9 || visibleIdsRef.current.has(item.id))}
onSelect={() => setSelectedItem(item)}
/>
),
[selectedId],
[selectedId, isFocused],
)
const keyExtractor = useCallback((item: MediaItem) => item.id, [])

View File

@ -1,4 +1,5 @@
import { AntDesign, EvilIcons, FontAwesome, Ionicons, MaterialCommunityIcons } from '@expo/vector-icons'
import { useIsFocused } from '@react-navigation/native'
import { Block, ConfirmModal, Img, Input, ListEmpty, Text, Toast, VideoBox } from '@share/components'
import { FlashList } from '@shopify/flash-list'
import * as ImagePicker from 'expo-image-picker'
@ -47,6 +48,9 @@ const Sync = observer(() => {
const [isSelectionMode, setIsSelectionMode] = useState(false)
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set())
// 直接使用 useIsFocused hook页面失焦时不渲染列表项以减少内存占用
const isFocused = useIsFocused()
const { connectedDevice, isScanning, transferProgress } = bleStore.state
const itemWidth = Math.floor((screenWidth - 12 * 2 - 12 * 2) / 3)
@ -380,16 +384,18 @@ const Sync = observer(() => {
isSelectionMode={isSelectionMode}
itemWidth={itemWidth}
post={post}
// 页面失焦时不渲染,减少内存占用
isVisible={isFocused}
onSelect={handleItemSelect}
key={post?.id}
/>
)
},
[isSelectionMode, selectedIds, selectedItem, itemWidth, handleItemSelect],
[isSelectionMode, selectedIds, selectedItem, itemWidth, handleItemSelect, isFocused],
)
return (
<Block className="relative flex-1">
<Block className="relative flex-1 bg-black">
<BannerSection />
<Block className="z-10 flex-1">
@ -658,12 +664,14 @@ const GridItem = memo(
isSelected,
isSelectionMode,
itemWidth,
isVisible = true,
onSelect,
}: {
post: any
isSelected: boolean
isSelectionMode: boolean
itemWidth: number
isVisible?: boolean
onSelect: (post: any) => void
}) => {
// 渲染状态标记
@ -707,7 +715,7 @@ const GridItem = memo(
style={{ transform: [{ skewX: '-6deg' }], height: itemWidth, width: itemWidth }}
>
<Block style={{ height: itemWidth, width: itemWidth }}>
<Img className="size-full" src={post.imageUrl} />
{isVisible && <Img className="size-full" src={post.imageUrl} />}
</Block>
{isSelected && <Block className="absolute inset-0 border-[3px] border-accent" />}

View File

@ -1,3 +1,4 @@
import { useIsFocused } from '@react-navigation/native'
import { memo, useEffect, useState } from 'react'
import Svg, { Circle, Defs, Pattern, Rect } from 'react-native-svg'
@ -19,6 +20,9 @@ const BannerSection = memo<BannerProps>(function Banner({ bgVideo }) {
setBgurl(bgVideo)
}, [bgVideo])
const isFocused = useIsFocused()
if (!isFocused) return null
return (
<Block className="absolute inset-0 z-0 overflow-hidden">
{/* 使用较小的分辨率降低内存占用 */}