import React from 'react';
import { Text, View } from 'react-native';
import { FlashList } from '@shopify/flash-list';
interface Item {
id: string;
title: string;
}
const MyList = ({ data }: { data: Item[] }) => {
return (
<FlashList
data={data}
renderItem={({ item }) => (
<View style={{ height: 80, padding: 10 }}>
<Text>{item.title}</Text>
</View>
)}
// Parametrul critic: dacă e greșit, apar layout jumps
estimatedItemSize={80}
keyExtractor={(item) => item.id}
/>
);
};Am avut recent de optimizat o aplicație de livrări unde curierii aveau de parcurs liste gigantice, de peste 3.000 de iteme, pe telefoane Android ultra-buget. Pe simulatorul meu de macOS totul rula impecabil, dar pe un Samsung A14 de 600 de lei, FlatList-ul clasic transforma aplicația într-un slideshow de 15 FPS.
Atunci am decis să arunc în luptă FlashList, biblioteca celor de la Shopify. Am făcut măsurători concrete folosind Perf Monitor-ul din developer menu și am vrut să văd dacă promisiunile lor de marketing chiar stau în picioare pe hardware slab.
De ce moare FlatList pe dispozitive ieftine?
Problema cu FlatList nu e neapărat codul din React Native, ci conceptul de bază. FlatList randează elementele pe măsură ce navighezi, iar pe cele care ies din ecran le demontează (unmount) pentru a elibera memorie. Pe un CPU lent, acest ciclu continuu de mount/unmount pune o presiune uriașă pe puntea (bridge-ul) dintre JS și Native.
Când curierul dădea un scroll rapid, JS thread-ul scădea instant la 5-8 FPS. Practic, UI-ul îngheța pentru că motorul JS nu mai putea procesa evenimentele de scroll în timp ce încerca să creeze noi noduri de view-uri native.
FlashList și magia reciclării
Spre deosebire de FlatList, FlashList folosește conceptul de „recycling”, împrumutat din dezvoltarea nativă de Android (RecyclerView). În loc să distrugă componentele care ies din ecran, le păstrează în memorie și doar le schimbă datele (prop-urile) când apar elemente noi în listă.
Trecerea a fost extrem de simplă, practic am înlocuit importul și am adăugat o singură proprietate obligatorie: estimatedItemSize.
Cifrele din Perf Monitor (Test real pe Samsung A14)
Am rulat ambele variante în mod de producție (release build), nu în dev, ca să am cifre curate. Rezultatele m-au convins pe loc:
- FlatList (Scroll rapid): UI Thread: 32 FPS | JS Thread: 11 FPS. Ecranul rămânea alb (blank spaces) secunde bune până când se randau elementele.
- FlashList (Scroll rapid): UI Thread: 58 FPS | JS Thread: 52 FPS. Zero ecrane albe, tranziție extrem de fluidă.
Pe lângă FPS, memoria RAM consumată a scăzut cu aproximativ 35 MB în cazul FlashList, deoarece numărul total de view-uri native instanțiate a rămas constant, indiferent de lungimea listei.
Nu totul e perfect: Trade-off-ul sincer
Deși pare soluția ideală, FlashList vine cu o problemă sâcâitoare: estimatedItemSize. Dacă pui o valoare greșită sau dacă elementele tale au înălțimi extrem de variate (de exemplu, unele au o imagine mare, altele doar un rând de text), o să observi mici sărituri (layout jumps) în timpul scroll-ului.
Dacă ai liste simple, cu înălțime fixă sau predictibilă, FlashList e de departe câștigătorul. Pentru liste complexe, dinamice, trebuie să bifezi cu atenție dimensiunile estimate, altfel experiența devine ciudată pentru utilizator.
Voi ați trecut complet pe FlashList în proiectele de producție sau ați rămas la FlatList-ul clasic din cauza problemelor de layout?