import React, { useCallback } from 'react';
import { View, Text, Image, StyleSheet } from 'react-native';
import { FlashList } from '@shopify/flash-list';
type Product = {
id: string;
title: string;
price: string;
type: 'banner' | 'product';
};
export const ProductFeed = ({ products }: { products: Product[] }) => {
const renderItem = useCallback(({ item }: { item: Product }) => {
if (item.type === 'banner') {
return <View style={styles.banner}><Text>{item.title}</Text></View>;
}
return (
<View style={styles.card}>
<Text style={styles.title}>{item.title}</Text>
<Text>{item.price}</Text>
</View>
);
}, []);
return (
<FlashList
data={products}
renderItem={renderItem}
// Esențial pentru optimizare când ai mai multe tipuri de carduri:
getItemType={(item) => item.type}
// Setează o valoare cât mai aproape de media reală în px:
estimatedItemSize={140}
keyExtractor={(item) => item.id}
/>
);
};
const styles = StyleSheet.create({
card: { height: 140, padding: 12, borderBottomWidth: 1 },
banner: { height: 80, backgroundColor: '#f0f0f0', padding: 8 },
title: { fontWeight: 'bold' }
});Aproape fiecare aplicație React Native în care am lucrat ajunge, mai devreme sau mai târziu, să sufere din cauza listelor pe Android. Tocmai am terminat un refactoring la un feed de produse pentru o aplicație cu peste 12k utilizatori activi zilnic, unde pe iOS totul mergea brici, dar pe telefoanele Android ieftine aveam plângeri constante de la clienți că aplicația „agăța”. Am pus un Moto G20 (3GB RAM, procesor Unisoc T700) în debug, am pornit Perf Monitor și am făcut benchmark direct între FlatList și FlashList.
Ce se întâmplă sub capotă la scroll
Problema clasică la FlatList este că unmount-ează componentele când ies din ecran și creează altele noi când dai scroll în jos. Pe un flagship nu simți asta, dar pe un procesor ieftin din seria MediaTek sau Unisoc, instanțierea repetată de Views din nativ pune la pământ thread-ul de JS și trimite garbage collector-ul în hyperdrive.
Pe Moto G20, într-un feed de 1.200 de produse cu imagini și badge-uri, măsurătorile mele cu Perf Monitor pe FlatList arătau groaznic la un scroll rapid:
- JS FPS: Cădea frecvent la 12-14 FPS (agăța vizibil).
- UI FPS: Oscila între 25 și 35 FPS.
- RAM: Spikuri constante când GC-ul încerca să curețe componentele distruse.
Rezultatele după trecerea pe FlashList
Shopify a scris FlashList având ca model RecyclerView-ul nativ din Android. În loc să distrugă componentele când ies din viewport, FlashList le reciclează și le populează cu date noi. Adică nu mai plătești costul de re-creare de Views în nativ, ci doar re-render de React pe date noi.
După ce am înlocuit FlatList cu <FlashList estimatedItemSize={120} />, cifrele s-au schimbat radical pe același Moto G20:
- JS FPS: A rămas stabil între 52 și 60 FPS, chiar și la scroll agresiv.
- UI FPS: S-a blocat la 58-60 FPS.
- Memorie: Utilizarea de RAM a rămas aproape plată, fără spikuri-le de Garbage Collector.
- Timp de primă randare: Am economisit aproximativ 35% la prima încărcare a listei.
Marea problemă: Trade-off-urile de care nu-ți zice nimeni
Tranziția nu e 1:1 și FlashList nu e o magie neagră fără defecte. Dacă ai iteme de dimensiuni extrem de dinamice — de exemplu, carduri unde textul se poate extinde la tap sau imagini cu aspect ratio necunoscut — FlashList o ia razna.
Dacă dai un estimatedItemSize greșit (de exemplu pui 80 în loc de 220), vei vedea sărituri urâte la scroll („layout jumps”) sau zone albe care apar câteva ms până când se calculează poziția. Am pățit la un proiect să am carduri de tipuri diferite și a trebuit să implementez getItemType manual. Dacă nu specifici tipul de item, FlashList reciclează un layout de tip A pentru date de tip B, ceea ce duce la re-render-uri complet inutile și anulează tot câștigul de performanță.
Concluzia mea
Dacă ai o listă scurtă, sub 50-100 de iteme simple, rămâi pe FlatList din React Native core — n-are rost să adaugi o dependență în plus. Dar dacă faci feed-uri infinite, e-commerce sau chat și ai cel puțin 15% utilizatori pe Android entry-level, FlashList e obligatoriu.
Voi cum gestionați feed-urile dinamice pe Android? Ați întâmpinat probleme cu layout jumps în FlashList?