import React, { memo } from 'react';
import { FlashList } from '@shopify/flash-list';
import { ProductCard, type Product } from './ProductCard';
interface CatalogProps {
items: Product[];
onAddToCart: (id: string) => void;
}
export const ProductCatalog = memo(({ items, onAddToCart }: CatalogProps) => {
return (
<FlashList
data={items}
renderItem={({ item }) => (
<ProductCard product={item} onAdd={onAddToCart} />
)}
// Măsoară exact în layout inspector, nu aproxima din ochi
estimatedItemSize={284}
keyExtractor={(item) => item.id}
// Evită layout jumps pe imagini necunoscute
drawDistance={250}
/>
);
});Pe un iPhone 15 Pro sau pe un Pixel recent, orice listă din React Native zboară indiferent cât de prost e scrisă. Problema apare când clientul testează release-ul pe un Samsung Galaxy A13 cu 3GB RAM și începe să urle că aplicația agață la fiecare swipe. Am măsurat comportamentul exact pe un astfel de device entry-level, iar cifrele dor.
Testul pe viu: 1.200 de carduri de produs
Pentru benchmark am folosit ecranul de catalog dintr-o aplicație de retail: 1.200 de elemente, fiecare card având o imagine remote, două taguri text, preț vechi/nou și un buton de adăugat în coș. Am activat Perf Monitor din Dev Menu și am dat scroll rapid timp de 10 secunde.
Cu FlatList nativ (chiar și optimizat cu removeClippedSubviews, maxToRenderPerBatch={10} și windowSize={5}):
- UI Thread: a oscilat între 32 și 44 FPS.
- JS Thread: s-a prăbușit rapid la 14-19 FPS.
- Experiență: spații albe masive la derulare rapidă; lista pur și simplu nu mai desena nimic timp de o secundă întreagă dacă trăgeai tare de ecran.
Am rescris ecranul cu @shopify/flash-list:
- UI Thread: stabil la 58-60 FPS.
- JS Thread: n-a scăzut sub 52 FPS în timpul aceluiași scroll agresiv.
- Memorie: consumul a rămas plat în loc să crească treptat pe măsură ce avansam în listă.
De ce crapă FlatList pe hardware slab
FlatList creează și distruge componente React la foc continuu. Când un element iese din viewport, FlatList îl demontează, iar când derulezi înapoi, montează o componentă complet nouă. Pe un procesor low-end precum Exynos 850, garbage collector-ul intră în fibrilații, iar operațiunile de mount pun bridge-ul sau noul renderer în genunchi.
FlashList nu distruge nimic. Împrumută logica de RecyclerView din Android: păstrează în memorie un număr fix de vederi native și doar le repopulează cu date noi când devin vizibile. Diferența de calcul este gigantică pentru un procesor lent.
Capcana reciclării (unde te frigi garantat)
FlashList nu este însă o soluție magică de tip drop-in fără bătăi de cap, oricât ar pretinde documentația lor.
Cea mai frecventă problemă apare dacă ții state intern în celulele din listă. Dacă ai un useState pentru un toggle sau o animație locală în interiorul cardului, când FlashList reciclează celula pentru elementul #40, acel state rămâne setat de la elementul #3. Te trezești că un card apare selectat din senin fără ca userul să fi apăsat pe el. Tot state-ul vizual trebuie să vină strict din props.
A doua capcană este estimatedItemSize. Dacă pui o valoare din burtă (de exemplu 100 când cardul are de fapt 280px), vei vedea sărituri ciudate de scroll și glitch-uri vizuale care sunt aproape imposibil de debugat ulterior.
Voi mai folosiți FlatList clasic în producție pe proiecte mari sau ați trecut direct pe FlashList peste tot?