import { FlashList } from "@shopify/flash-list";
const MyList = ({ data }) => {
return (
<FlashList
data={data}
renderItem={({ item }) => <Card item={item} />}
// Valoarea asta e critică pentru a evita jank-ul
estimatedItemSize={250}
// Evită folosirea index-ului aici
keyExtractor={(item) => item.id.toString()}
/>
);
};Dacă ai lucrat la o aplicație de React Native cu liste lungi, știi deja că Android-ul entry-level e cel mai mare inamic. Pe un iPhone 15 totul pare smooth, dar pe un Samsung A12 sau un Xiaomi de acum 3 ani, FlatList-ul începe să gâfâie rapid. Recent, am avut de optimizat un feed de tip social media cu vreo 10.000 de itemi, fiecare cu imagini, text și butoane de interacțiune, și am zis că e momentul să fac trecerea la FlashList de la Shopify.
Problema cu FlatList și de ce agață
FlatList funcționează prin montarea și demontarea componentelor pe măsură ce utilizatorul face scroll. Când dai scroll rapid, bridge-ul de React Native e bombardat cu mesaje de tipul „distruge componenta asta, randează-o pe cealaltă”. Pe un procesor slab, asta duce la celebrul „white screen” în timp ce aștepți ca JS-ul să prindă din urmă scroll-ul nativ.
La proiectul meu, cu Perf Monitor deschis, vedeam cum JS thread-ul scădea la 15-20 FPS imediat ce începeam să dau scroll agresiv. FlatList e ok pentru liste scurte sau UI simplu, dar devine un coșmar când ai layout-uri complexe. Problema e că garbage collector-ul muncește în disperare să curețe componentele demontate, iar memoria RAM pe dispozitivele astea ieftine e oricum limitată.
De ce FlashList schimbă jocul
FlashList nu distruge componentele, ci le reciclează. E conceptul de „ViewHolder” din Android-ul nativ, adus în React Native. În loc să creeze un nod nou în ierarhia de view-uri, el ia un card care tocmai a ieșit din ecran și îi schimbă doar datele (props-urile) pentru a-l refolosi jos.
După ce am făcut migrarea, am rămas mască. Pe același Samsung A12 obosit, unde FlatList se chinuia la 20 FPS, FlashList a stat constant în 58-60 FPS. Am economisit cam 30% din timpul de procesare pe JS thread pentru că nu mai aveam overhead-ul de mount/unmount. Utilizatorul simte imediat diferența; scroll-ul e „lipit” de deget, fără acele sacadări enervante.
Trade-off-uri și bătăi de cap
Nu e totul roz, că altfel nu mai vorbeam despre asta. Cea mai mare problemă e estimatedItemSize. Dacă nu-i dai o valoare cât mai apropiată de realitate, lista o să facă niște salturi vizuale (jumps) extrem de urâte când randează elemente noi.
Un alt aspect e layout-ul. Dacă itemii tăi au înălțimi foarte diferite, FlashList s-ar putea să se încurce în calcule și să lase spații goale. Eu am rezolvat-o calculând o medie ponderată a înălțimilor, dar tot a durat vreo 2 ore de debugging să înțeleg de ce lista mea „sărea” 50 de pixeli în sus din senin. De asemenea, trebuie să ai mare grijă la keyExtractor și să nu folosești index-ul ca cheie, altfel reciclarea o să facă un haos total în UI-ul tău, afișând datele greșite în celulele reciclate.
FlashList e clar câștigătorul pentru orice listă care trece de 50 de elemente pe Android, dar pentru un meniu simplu de setări, FlatList rămâne mai safe și mai ușor de implementat. Voi ce folosiți pentru listele complexe, ați rămas pe FlatList cu optimizări de windowSize sau ați trecut la FlashList?