refactor(native): replace FlatList with FlashList for search and person screens

This commit is contained in:
2026-03-14 14:36:37 -04:00
parent 9b29da21bf
commit 76648a6023
5 changed files with 26 additions and 25 deletions
+1
View File
@@ -25,6 +25,7 @@
"@orpc/tanstack-query": "catalog:",
"@react-native-menu/menu": "2.0.0",
"@react-navigation/elements": "2.9.10",
"@shopify/flash-list": "2.3.0",
"@sofa/api": "workspace:*",
"@sofa/tmdb": "workspace:*",
"@tabler/icons-react-native": "3.40.0",
@@ -1,7 +1,8 @@
import { FlashList } from "@shopify/flash-list";
import { useMutation, useQuery } from "@tanstack/react-query";
import { Stack, useRouter } from "expo-router";
import { useCallback, useMemo, useRef, useState } from "react";
import { FlatList, View } from "react-native";
import { View } from "react-native";
import Animated, { FadeIn } from "react-native-reanimated";
import { RecentlyViewedList } from "@/components/search/recently-viewed-list";
import {
@@ -154,7 +155,7 @@ export default function SearchScreen() {
</Text>
</Animated.View>
) : (
<FlatList
<FlashList
data={allResults}
keyExtractor={keyExtractor}
renderItem={renderItem}
+16 -21
View File
@@ -1,3 +1,4 @@
import { FlashList } from "@shopify/flash-list";
import {
IconAlertTriangle,
IconCalendar,
@@ -9,7 +10,7 @@ import { useQuery } from "@tanstack/react-query";
import { format, parseISO } from "date-fns";
import { Stack, useLocalSearchParams, useRouter } from "expo-router";
import { useCallback, useEffect } from "react";
import { FlatList, Pressable, useWindowDimensions, View } from "react-native";
import { Pressable, useWindowDimensions, View } from "react-native";
import Animated, { FadeIn, FadeInDown } from "react-native-reanimated";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useCSSVariable } from "uniwind";
@@ -83,7 +84,7 @@ export default function PersonDetailScreen() {
const renderFilmographyItem = useCallback(
({ item: credit }: { item: (typeof filmography)[number] }) => (
<View style={{ width: columnWidth }}>
<View style={{ width: columnWidth, paddingBottom: FILMOGRAPHY_GAP }}>
<PosterCard
id={credit.titleId}
tmdbId={credit.tmdbId}
@@ -271,25 +272,19 @@ export default function PersonDetailScreen() {
headerTitle: "",
}}
/>
<FlatList
data={filmography}
keyExtractor={(item) => item.titleId}
renderItem={renderFilmographyItem}
numColumns={2}
columnWrapperStyle={{
gap: FILMOGRAPHY_GAP,
paddingHorizontal: FILMOGRAPHY_PADDING,
}}
contentContainerStyle={{
paddingBottom: insets.bottom + 32,
gap: FILMOGRAPHY_GAP,
}}
ListHeaderComponent={listHeader}
className="bg-background"
initialNumToRender={6}
maxToRenderPerBatch={8}
windowSize={5}
/>
<View className="flex-1 bg-background">
<FlashList
data={filmography}
keyExtractor={(item) => item.titleId}
renderItem={renderFilmographyItem}
numColumns={2}
contentContainerStyle={{
paddingBottom: insets.bottom + 32,
paddingHorizontal: FILMOGRAPHY_PADDING,
}}
ListHeaderComponent={listHeader}
/>
</View>
</>
);
}
@@ -1,7 +1,8 @@
import { FlashList } from "@shopify/flash-list";
import { IconHistory, IconSearch } from "@tabler/icons-react-native";
import { useRouter } from "expo-router";
import { useCallback } from "react";
import { Alert, FlatList, Pressable, View } from "react-native";
import { Alert, Pressable, View } from "react-native";
import Animated, { FadeIn, FadeInDown } from "react-native-reanimated";
import { useCSSVariable } from "uniwind";
import { RecentlyViewedRow } from "@/components/search/recently-viewed-row";
@@ -84,7 +85,7 @@ export function RecentlyViewedList() {
return (
<Animated.View entering={FadeIn.duration(400)} className="flex-1">
<FlatList
<FlashList
data={items}
keyExtractor={keyExtractor}
renderItem={renderItem}
+3
View File
@@ -25,6 +25,7 @@
"@orpc/tanstack-query": "catalog:",
"@react-native-menu/menu": "2.0.0",
"@react-navigation/elements": "2.9.10",
"@shopify/flash-list": "2.3.0",
"@sofa/api": "workspace:*",
"@sofa/tmdb": "workspace:*",
"@tabler/icons-react-native": "3.40.0",
@@ -981,6 +982,8 @@
"@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="],
"@shopify/flash-list": ["@shopify/flash-list@2.3.0", "", { "peerDependencies": { "@babel/runtime": "*", "react": "*", "react-native": "*" } }, "sha512-DR7VuN8KJHTYj9zv1/IhpqrMBMQyeeW/DCWCbVQAAkWhHrc6ylIbXOY+qK93CuHABV+dNHXK/3V6p4wCSW/+wA=="],
"@sinclair/typebox": ["@sinclair/typebox@0.27.10", "", {}, "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA=="],
"@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="],