import React, { useCallback } from 'react';
import { Text, TouchableOpacity } from 'react-native';
import { FlashList } from '@shopify/flash-list';
interface Item {
id: string;
title: string;
}
// Memoizăm celula pentru a preveni rerandările inutile
const ListItem = React.memo(({ item, onPress }: { item: Item; onPress: (id: string) => void }) => {
return (
<TouchableOpacity onPress={() => onPress(item.id)} style={{ height: 80, padding: 16 }}>
<Text>{item.title}</Text>
</TouchableOpacity>
);
});
ListItem.displayName = 'ListItem';
export const MyList = ({ data }: { data: Item[] }) => {
// Referință stabilă pentru funcția de click
const handlePress = useCallback((id: string) => {
console.log('Pressed item:', id);
}, []);
const renderItem = useCallback(({ item }: { item: Item }) => (
<ListItem item={item} onPress={handlePress} />
), [handlePress]);
return (
<FlashList
data={data}
renderItem={renderItem}
estimatedItemSize={80}
keyExtractor={(item) => item.id}
/>
);
};Salutare tuturor. Văd des pe forum oameni care se plâng de lag în React Native când dau scroll în liste lungi. Am pățit și eu asta pe un proiect cu 12k useri activi, unde feed-ul scădea sub 25 FPS, dar am rezolvat-o fără să rescriu cod în Java sau Swift. Hai să vedem cum să evităm optimizările premature și ce funcționează cu adevărat în producție.
Mitul cu "pune useMemo peste tot"
La început, din disperare, am început să pun useMemo și useCallback la absolut orice funcție și variabilă. Mare greșeală. Ambele hook-uri vin cu un cost mic, dar real, de memorie și execuție. Motorul de JS trebuie să compare array-ul de dependențe la fiecare randare.
Regula mea e simplă acum: folosesc useCallback doar dacă acea funcție este pasată ca prop unui child component memorat cu React.memo. Dacă child-ul nu e învelit în React.memo, degeaba îi dai tu aceeași referință de funcție; el oricum se va reranda fiindcă părintele lui se rerandează.
Pentru calcule grele, cum ar fi filtrarea sau maparea unui array de 500 de elemente după 3 criterii, useMemo își scoate banii. Altfel, pentru concatenat două string-uri sau operații simple, e doar overhead inutil și cod mai greu de citit.
FlatList vs. Shopify's FlashList
Dacă aveți liste lungi sau complexe, recomandarea mea e să renunțați la clasicul FlatList. Pe proiectul menționat, simpla trecere la FlashList de la Shopify a crescut frame rate-ul instant pe un Android low-end de acum 4 ani.
Cum funcționează magia? FlatList distruge componentele care ies din ecran și creează altele noi când dai scroll înapoi. Asta înseamnă garbage collection la greu și lag. FlashList reciclează celulele de memorie direct la nivel nativ.
Totuși, există un trade-off enervant: trebuie să îi dai un estimatedItemSize cât mai precis. Dacă pui o valoare complet greșită, lista o să sară urât la scroll (layout jumps) pentru că motorul își recalculează dimensiunile din mers. Eu de obicei randez lista, mă uit în consolă la avertismentele FlashList și folosesc media sugerată de ei.
Cum legăm totul cap la cap
Chiar și cu FlashList, dacă celula ta se rerandează complet când dai tap pe o singură inimioară de "Like", tot ai o problemă de performanță. Soluția este să spargi celula într-o componentă separată, să o învelești în React.memo și să pasezi callback-urile cu referințe stabile.
Prin separarea asta curată, React Native va actualiza doar rândul pe care s-a dat click, lăsând restul listei intact în memorie.
Voi cum abordați optimizarea listelor în React Native? Ați rămas pe FlatList cu un windowSize micșorat sau ați trecut deja pe FlashList?