import React from 'react';
import { Text, View } from 'react-native';
import { FlashList } from '@shopify/flash-list';
interface Product { id: string; title: string; price: string; }
export const ProductList = ({ products }: { products: Product[] }) => {
return (
<FlashList
data={products}
renderItem={({ item }) => (
<View style={{ height: 120, padding: 16 }}>
<Text>{item.title}</Text>
<Text>{item.price}</Text>
</View>
)}
estimatedItemSize={120} // Foarte important! Pune valoarea medie reală
keyExtractor={(item) => item.id}
/>
);
};Am avut recent de optimizat un catalog de produse cu vreo 4.000 de iteme pe o aplicație React Native. Pe un iPhone de generație nouă totul rula impecabil, dar pe un Samsung Galaxy A12 de 600 de lei, FlatList-ul scotea un penibil 15 FPS la scroll rapid. Am trecut la FlashList de la Shopify și vreau să vă arăt exact cifrele din Perf Monitor și de ce merită să faceți trecerea.
De ce moare FlatList pe Android ieftin
Problema cu FlatList-ul clasic din React Native este că nu face reciclare reală de view-uri native. Când dai scroll, el montează și demontează componente din arborele de React. Pe un telefon slab, treaba asta pune o presiune uriașă pe CPU și pe Garbage Collector-ul din Android.
Am pornit Perf Monitor-ul din Developer Menu pe Samsung-ul ăla chinuit și am început să dau scroll ca un user disperat.
Rezultatele pe FlatList:
- UI Thread: Scădea constant pe la 25-30 FPS.
- JS Thread: Se prăbușea la 8-12 FPS. Ecranul pur și simplu îngheța pentru o secundă-două, lăsând zone albe mari în spate pentru că thread-ul de JS nu apuca să randeze destul de repede celulele noi.
Cum rezolvă FlashList problema
FlashList, librăria celor de la Shopify, folosește reciclare reală. În loc să distrugă componentele care ies din ecran, le păstrează în memorie și doar le injectează datele noi. E fix conceptul de RecyclerView din Android nativ.
După ce am înlocuit FlatList cu FlashList, am rulat același test pe același telefon de 600 de lei.
Rezultatele pe FlashList:
- UI Thread: Stabil la 55-58 FPS.
- JS Thread: Rămânea pe la 48-52 FPS, chiar și la scroll agresiv.
Zontele albe au dispărut aproape complet. Experiența s-a simțit instant mult mai nativă și mai fluidă.
Trade-off-ul sincer: Nu e totul doar lapte și miere
Deși sună ca un glonț de argint, FlashList vine cu niște compromisuri de care m-am lovit și eu la implementare:
- Proprietatea
estimatedItemSizeeste obligatorie. Trebuie să îi dai o valoare cât mai apropiată de realitate. Dacă înălțimea itemelor tale variază masiv (de exemplu, unele au 100px, altele 400px), FlashList se va chinui cu layout-ul și vei vedea niște sărituri urâte (layout shifts) în timpul scroll-ului. - Atenție la state-ul intern. Pentru că celulele sunt reciclate, dacă ai un state local în interiorul componentei de item (de exemplu, un buton de
isLiked), starea respectivă poate rămâne activă și pe item-ul nou reciclat dacă nu ești atent cum o resetezi în funcție de props.
Mai jos aveți un exemplu simplu de cum se face trecerea. E aproape drop-in replacement, dar cheia stă în acel estimatedItemSize pe care l-am calculat ca medie a înălțimilor listei noastre.