eduardweb.
React Native & ExpoIntermediar#performance#react-native#shopify-flashlist#android

React Native FlatList vs FlashList: Cum am salvat FPS-ul pe un Android de 600 lei

De Delia Petre, 21 mai 2026 · 5 vizualizări · 3 like-uri

Postat 21 mai 2026
typescript
import React from 'react';
import { Text, View } from 'react-native';
import { FlashList } from '@shopify/flash-list';

interface Product { 
  id: string; 
  title: string; 
  price: string; 
}

const ProductList = ({ items }: { items: Product[] }) => {
  return (
    <FlashList
      data={items}
      renderItem={({ item }) => (
        <View style={{ height: 80, padding: 10 }}>
          <Text>{item.title}</Text>
          <Text>{item.price}</Text>
        </View>
      )}
      // critic pentru performanță pe Android ieftin
      estimatedItemSize={80} 
      keyExtractor={(item) => item.id}
    />
  );
};

Am avut de optimizat un feed infinit de produse pentru o aplicație de curierat, rulată pe un Android ieftin de 600 de lei (un Motorola E13 cu doar 2GB RAM). FlatList-ul nativ pur și simplu îngenunchea CPU-ul la scroll rapid, coborând sub 15 FPS pe JS thread. Trecerea la FlashList ne-a salvat livrarea proiectului, ridicând performanța la un 55-58 FPS stabil, dar am plătit o taxă de refactoring destul de enervantă.

De ce moare FlatList pe hardware ieftin?

Problema cu FlatList din React Native este că montează și demontează componente pe măsură ce dai scroll. Pe un flagship nu simți asta deloc. Însă pe un procesor entry-level, garbage collector-ul intră în panică din cauza alocărilor masive de memorie. Când userul dă scroll rapid, JS thread-ul se blochează încercând să randeze noile elemente în timp ce le distruge pe cele vechi.

Cu Perf Monitor-ul din meniul de developer activat, am văzut cum UI thread-ul rămânea la 60 FPS (că na, se mișca spinner-ul), dar JS thread-ul murea la un penibil 12 FPS. Aplicația părea complet înghețată timp de 2-3 secunde, timp în care ecranul rămânea alb.

Soluția Shopify: FlashList

Băieții de la Shopify au regândit complet conceptul. În loc să distrugă componentele care ies din ecran, FlashList le reciclează (similar cu RecyclerView din Android nativ). Practic, structura de view-uri rămâne pe ecran, se schimbă doar datele din ele.

Pe același Motorola obosit, am obținut instant un boost masiv: JS thread-ul nu a mai scăzut sub 52 FPS în timpul scroll-ului agresiv. Memoria RAM consumată a scăzut cu vreo 40MB, fiindcă nu mai cream sute de noduri de view-uri noi în memorie.

Trade-off-ul sincer: Nu este drop-in replacement

Deși pe site-ul lor scrie că doar schimbi numele componentei, în realitate te lovești de două mari probleme:

  1. estimatedItemSize: FlashList are nevoie absolută de această proprietate ca să-și calculeze corect dimensiunea scrollbar-ului și să știe câte elemente să randeze inițial. Dacă pui o valoare greșită, o să ai "layout jumps" (sărituri vizuale) extrem de deranjante la scroll. Am pierdut vreo 4 ore calibrând asta pe diferite dimensiuni de ecran.
  2. Reciclarea stării interne: Dacă item-ul tău din listă are un state intern (de exemplu, un buton de isExpanded local), acel state va fi reținut când view-ul este reciclat pentru alt produs. Te trezești că dai scroll și vezi produse deja expandate deși nu ai dat click pe ele. Soluția? Trebuie să muți orice stare în store-ul global sau în array-ul de date.

Dacă aveți liste simple, FlatList e sfânt și n-are rost să adăugați o dependență nativă în plus. Dar dacă treceți de 200 de itemi cu imagini pe dispozitive low-end, FlashList e singura șansă ca utilizatorii să nu vă șteargă aplicația.

Voi ce experiențe aveți cu FlashList? V-ați lovit de bug-uri de randare din cauza reciclării de view-uri?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.