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

FlatList vs FlashList pe Android-uri ieftine: Ce-am învățat când am scăpat de 18 FPS

De Andreea Crăciun, 12 aug. 2026 · 7 vizualizări · 3 like-uri

Postat acum 4 zile
typescript
import React from 'react';
import { FlashList } from '@shopify/flash-list';
import { ProductCard, Product } from './ProductCard';

interface Props {
  products: Product[];
}

export const ProductCatalog = ({ products }: Props) => {
  return (
    <FlashList
      data={products}
      renderItem={({ item }) => <ProductCard product={item} />}
      estimatedItemSize={280}
      keyExtractor={(item) => item.id}
      overrideItemLayout={(layout, item) => {
        // Oțional: ajustare dacă ai tipuri diferite de carduri
        if (item.isFeatured) layout.size = 360;
      }}
    />
  );
};

Problema de la care a plecat totul

Anul trecut lucram la un e-commerce local. Aveam un catalog cu peste 15.000 de produse, filtre dinamice și carduri destul de încărcate: imagini, badge-uri de discount și stoc live. Pe iPhone 13-le meu de teste totul mergea brici, 60 FPS fluide. Când am pus însă aplicația pe un Samsung A12 cu 3GB RAM — telefonul tipic pe care îl are o bună parte din baza de useri din RO —, dezastru. Scroll-ul agăța grav, iar din Perf Monitor vedeam cum JS FPS scădea dramatic la 12-18 frame-uri.

FlatList-ul nativ din React Native tinde să cedeze pe Android low-end pentru că unmount-ează și re-creează noduri React încontinuu. Irosește cicluri de CPU cu reconcilierea layout-ului pe thread-ul de JS, iar bridge-ul devine rapid gâtul de sticlă.

Trecerea la FlashList și numerele reale

Am decis să înlocuim FlatList cu @shopify/flash-list. Promisiunile din documentație sunau bine, dar aveam doza mea de scepticism. Spre deosebire de FlatList, FlashList folosește reciclare de view-uri pe bune (conceptul clasic de RecyclerView din Android nativ), în loc să distrugă și să creeze elemente la infinit.

După două ore de refactoring, am pornit din nou Perf Monitor pe Samsung-ul A12. Rezultatele au fost noaptea și ziua:

  • FlatList clasic: UI FPS ~42, JS FPS ~18 la scroll rapid. Zone albe (blank space) vizibile de câte jumătate de secundă.
  • FlashList: UI FPS 58-60, JS FPS 55-58. Zonele albe au dispărut aproape complet, chiar și la scroll agresiv.

Am obținut o experiență nativă pe cel mai ieftin telefon din birou, fără să fim nevoiți să regândim arhitectura de state a aplicației.

Unde se rupe filmul: Trade-off-uri sincere

Nu e totul numai lapte și miere. Când treci la FlashList, dai peste câteva capcane specifice de care te lovești la prima implementare:

  1. estimatedItemSize e critic: Dacă pui o valoare aiurea (de exemplu pui 100px și cardul are în realitate 350px), o să ai flicker urât pe ecran și scroll jumpy. Trebuie să măsori dimensiunea medie reală din UI.
  2. Reciclarea stării interne: Pentru că componentele se reciclează, dacă ai stare locală într-un card (de exemplu un const [isLiked, setIsLiked] = useState(false)), când elementul iese din viewport și e reciclat pentru alt produs, starea aia va persista dacă nu e derivată direct din props sau resetată manual.
  3. Layout-uri dinamice cu variații enorme: Dacă lista ta conține elemente cu înălțimi complet opuse (un banner de 400px urmat de un text de 20px), FlashList se chinuie la calculul offset-urilor.

Cum arată schimbarea în cod

Migrarea e simplă dacă ai deja un renderItem curat și izolat. În majoritatea cazurilor înlocuiești doar componenta și adaugi prop-ul de estimare.

Dacă ai o aplicație cu scroll infinit și mai mult de 20% din useri pe Android entry-level, optimizarea asta e probabil cel mai ieftin gain de performanță pe care îl poți bifa într-un sprint.

Voi ce folosiți în producție pe React Native în ultima vreme? Ați rămas pe FlatList tuns cu windowSize și maxToRenderPerBatch sau ați migrat deja pe FlashList?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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