import React from 'react';
import { Text, View } from 'react-native';
import { FlashList } from '@shopify/flash-list';
interface Product {
id: string;
title: string;
price: number;
}
const ProductList = ({ products }: { products: Product[] }) => {
return (
<FlashList
data={products}
renderItem={({ item }) => (
<View style={{ height: 120, padding: 16 }}>
<Text>{item.title}</Text>
<Text>{item.price} RON</Text>
</View>
)}
estimatedItemSize={120} // Valoare critică pentru a preveni layout shifts
keyExtractor={(item) => item.id}
/>
);
};Salutare! Am lucrat recent la un feed infinit pentru o aplicație de e-commerce unde utilizatorii scrollau prin mii de produse cu imagini mari și descrieri. Pe iPhone-ul de development totul rula la 120Hz de zici că era unt, dar când am pus build-ul pe un Samsung Galaxy A12 (un entry-level clasic de vreo 500 de lei), s-a produs dezastrul: frame drops masive, ecranul îngheța la scroll rapid și FPS-ul pe thread-ul de UI scădea vertiginos pe la 20-25.
Am decis să testez FlashList, librăria celor de la Shopify, ca să văd dacă promisiunile lor chiar stau în picioare pe hardware slab. Am pornit Perf Monitor-ul din meniul de developer și am început să dau scroll ca disperatul.
De ce moare FlatList când dai de greu?
FlatList-ul din React Native funcționează prin montarea și demontarea componentelor pe măsură ce ele intră și ies din viewport. Pe un procesor octa-core de buget, garbage collector-ul și recrearea de noduri în arborele de render pur și simplu îngenunchează thread-ul de JS. Când dai scroll rapid, FlatList nu ține pasul cu randarea și începi să vezi acele dreptunghiuri albe (blank areas) în timp ce JS-ul încearcă disperat să trimită noile elemente peste bridge.
Cifrele din Perf Monitor
Am făcut testele pe un feed de 500 de produse, fiecare card având o imagine (FastImage), un titlu, un preț și un buton de adăugare în coș.
Cu FlatList:
- UI FPS: Oscila între 25 și 42 FPS la scroll mediu. La scroll rapid, scădea la 15 FPS.
- JS FPS: Scădea constant sub 10 FPS.
- Blank areas: Foarte vizibile. Trebuia să aștept 1-2 secunde să se încarce imaginile și textul.
Cu FlashList:
- UI FPS: Stabil la 57-60 FPS în majoritatea timpului. Chiar și la cel mai rapid scroll, nu a scăzut sub 48 FPS.
- JS FPS: A rămas stabil în jur de 52-55 FPS.
- Blank areas: Aproape inexistente, randarea fiind aproape instantanee.
Secretul celor de la Shopify este reciclarea view-urilor. În loc să distrugă componentele vechi și să creeze altele noi, FlashList le păstrează structura nativă și doar le injectează noile date (recycling). Astfel, overhead-ul pe garbage collection scade dramatic.
Codul și marele compromis (Trade-off)
Migrarea e extrem de simplă deoarece API-ul este aproape identic cu cel de la FlatList. Totuși, există o capcană: proprietatea estimatedItemSize.
Dacă pui o valoare complet greșită aici, o să ai parte de un comportament bizar: layout shifts (sărituri de elemente la scroll) și scrollbar-ul o ia razna. Pentru a afla valoarea corectă, randează lista o dată, iar FlashList îți va printa în consolă o avertizare cu dimensiunea medie optimă.
Un alt trade-off important este legat de componentele cu stare internă. Deoarece celulele sunt reciclate, dacă ai o stare gen isExpanded definită în interiorul item-ului din listă, acea stare va fi moștenită de noul item care folosește celula reciclată. Trebuie să muți stările în store sau în componenta părinte.
La voi cum e? Mai folosește cineva FlatList pur pentru liste complexe în producție, sau ați trecut toți pe FlashList?