diff --git a/app/modules/home/HomeScreen.tsx b/app/modules/home/HomeScreen.tsx index f46cc2c..8b8bf10 100755 --- a/app/modules/home/HomeScreen.tsx +++ b/app/modules/home/HomeScreen.tsx @@ -1,8 +1,10 @@ -import React, { useCallback, type FC } from 'react'; -import { FlatList, ScrollView, View } from 'react-native'; +import React, { useCallback, useMemo, type FC } from 'react'; +import { ScrollView, View } from 'react-native'; +import { FlashList } from '@shopify/flash-list'; import { CustomButton, Spinner, Text } from '../../components'; import { Strings } from '../../constants'; import { useTheme } from '../../hooks'; +import { scale } from '../../theme'; import styleSheet from './HomeStyles'; import { CategoryChip } from './sub-components/category-chip'; import { CategoryChipShimmer } from './sub-components/category-chip-shimmer'; @@ -17,7 +19,7 @@ import type { Category, Product } from './HomeTypes'; * SafeAreaView (screenBackground) * └── HomeHeader (branded greeting) * └── ScrollView horizontal (category chip row — sticky) - * └── FlatList numColumns={2} (product grid or empty state) + * └── FlashList numColumns={2} (product grid or empty state) * * All state and logic delegated to useHome(). */ @@ -190,11 +192,24 @@ const HomeScreen: FC = (): React.ReactElement => { ); }, [shouldShowRefreshError, styles.productStatusContainer, styles.productStatusText]); - const productFeedback = - renderCategoryProductsLoadingState() || - renderCategoryProductRetryState() || - renderProductsLoadingState() || - renderProductRetryState(); + const productFeedback = useMemo( + () => + renderCategoryProductsLoadingState() || + renderCategoryProductRetryState() || + renderProductsLoadingState() || + renderProductRetryState(), + [ + renderCategoryProductsLoadingState, + renderCategoryProductRetryState, + renderProductsLoadingState, + renderProductRetryState + ] + ); + + const flashListContentContainerStyle = useMemo( + () => [styles.grid, filteredProducts.length === 0 ? styles.gridEmptyContent : undefined], + [filteredProducts.length, styles.grid, styles.gridEmptyContent] + ); return ( @@ -212,17 +227,13 @@ const HomeScreen: FC = (): React.ReactElement => { {renderCategoryStatus()} {renderProductStatus()} {productFeedback || ( - - removeClippedSubviews - columnWrapperStyle={filteredProducts.length > 0 ? styles.columnWrapper : undefined} - contentContainerStyle={[ - styles.grid, - filteredProducts.length === 0 ? styles.gridEmptyContent : undefined - ]} + + contentContainerStyle={flashListContentContainerStyle} data={filteredProducts} + // Estimated height of a ProductCard in the 2-column grid (image + info section), scaled to device. + estimatedItemSize={scale(300)} ListEmptyComponent={renderEmptyState} numColumns={2} - windowSize={5} keyExtractor={keyExtractor} refreshing={activeCategory === 'all' ? isProductsRefreshing : false} renderItem={renderProductItem} diff --git a/app/modules/home/HomeStyles.ts b/app/modules/home/HomeStyles.ts index bf3ea3c..a27be51 100755 --- a/app/modules/home/HomeStyles.ts +++ b/app/modules/home/HomeStyles.ts @@ -27,10 +27,6 @@ const styles = (theme: ThemeMode) => paddingHorizontal: scale(10), paddingTop: scale(4) }, - /** columnWrapperStyle for the 2-column FlatList */ - columnWrapper: { - justifyContent: 'space-between' - }, /** Empty-state container — centred vertically */ emptyContainer: { alignItems: 'center', @@ -56,7 +52,7 @@ const styles = (theme: ThemeMode) => fontSize: scale(15), textAlign: 'center' }, - /** FlatList contentContainerStyle */ + /** FlashList contentContainerStyle */ grid: { paddingBottom: scale(24), paddingHorizontal: scale(8) diff --git a/package.json b/package.json index 765a872..f862d91 100755 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "preset": "jest-expo" }, "dependencies": { + "@shopify/flash-list": "2.3.1", "@expo/config-plugins": "54.0.1", "@expo-google-fonts/rubik": "0.4.2", "@expo/vector-icons": "15.0.2",