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

De ce se blochează lista în React Native? Cum am trecut la FlashList și am salvat 60 FPS

De Vlad Stancu, 3 iun. 2026 · 2 vizualizări · 3 like-uri

Postat acum 6 zile
typescript
import React, { useCallback } from 'react';
import { Text, TouchableOpacity } from 'react-native';
import { FlashList } from '@shopify/flash-list';

// Componentă copil memorată corespunzător
const ProductItem = React.memo(({ id, title, onPress }) => {
  return (
    <TouchableOpacity onPress={() => onPress(id)} style={{ height: 80, padding: 10 }}>
      <Text>{title}</Text>
    </TouchableOpacity>
  );
});

export default function ProductList({ products }) {
  // useCallback este util pentru că ProductItem folosește React.memo
  const handlePress = useCallback((id) => {
    console.log('Product pressed:', id);
  }, []);

  const renderItem = useCallback(({ item }) => (
    <ProductItem id={item.id} title={item.title} onPress={handlePress} />
  ), [handlePress]);

  return (
    <FlashList
      data={products}
      renderItem={renderItem}
      estimatedItemSize={80}
      keyExtractor={(item) => item.id}
    />
  );
}

Salutare tuturor. Am tot văzut pe grupuri discuții despre performanța listelor în React Native și m-am gândit să vă povestesc din ce m-am lovit eu recent. Am avut de optimizat un feed de produse destul de stufos la o aplicație de e-commerce cu vreo 15.000 de utilizatori activi pe zi. Pe telefoanele de top totul părea perfect, dar pe un Android low-end, de vreo 700 de lei, experiența de scroll era un coșmar: frame-uri blocate, ecran alb secunde bune și o grămadă de nervi.

Am reușit să stabilizăm aplicația la 60 FPS constant după ce am regândit complet cum randăm listele și cum folosim hook-urile de memorare.

De ce moare FlatList când lista crește?

FlatList-ul nativ din React Native este, în esență, doar un wrapper peste un ScrollView. Când utilizatorul dă scroll rapid, componentele care ies din ecran sunt demontate, iar cele noi sunt montate. Acest proces de mount/unmount este extrem de costisitor pentru thread-ul de JS. În plus, garbage collector-ul trebuie să curețe constant memoria lăsată în urmă de componentele distruse, ceea ce provoacă acele micro-întreruperi (stutter).

Aici intervine FlashList, biblioteca dezvoltată de cei de la Shopify. Spre deosebire de FlatList, ea nu distruge componentele, ci le reciclează layout-ul nativ. Practic, refolosește aceleași view-uri și doar schimbă datele din ele.

La proiectul nostru, simpla trecere de la FlatList la FlashList a redus consumul de memorie RAM cu 35% în timpul scroll-ului și a eliminat complet momentele în care utilizatorul vedea ecrane albe. Totuși, există un trade-off important: FlashList are nevoie de un estimatedItemSize cât mai precis. Dacă dimensiunile elementelor din listă variază masiv (de exemplu, unele carduri au 100px înălțime, iar altele 400px), scroll-ul poate deveni ușor "săltăreț" (jumpy) deoarece motorul de reciclare nu poate calcula corect pozițiile.

Mitul useCallback și useMemo pe post de "pastilă magică"

Văd extrem de des cod unde dezvoltatorii pun useCallback pe absolut toate funcțiile și useMemo pe toate array-urile simple, sperând că asta va face aplicația mai rapidă. În realitate, de multe ori fac mai mult rău. Fiecare hook de memorare are un cost: alocare suplimentară de memorie și o verificare de dependențe la fiecare randare.

Regula mea este simplă: nu folosesc useCallback decât dacă acea funcție este pasată ca prop către o componentă copil care este memorată cu React.memo.

Dacă ai o listă în care fiecare rând este un element complex, trebuie să folosești React.memo pe componenta rândului. Dar atenție: dacă transmiți o funcție de callback direct în stilul onPress={() => handlePress(item.id)}, randarea se va declanșa oricum. Funcția inline se recreează la fiecare randare a părintelui, invalidând complet optimizarea adusă de React.memo. Aici este locul ideal unde useCallback își merită banii.

Cum arată o implementare curată

Cel mai bine funcționează o arhitectură în care componenta de item primește doar ID-ul și datele strict necesare, iar callback-ul este stabilizat. În secțiunea de cod de mai jos am lăsat un exemplu minimalist despre cum legăm corect un callback stabilizat de un item memorat în interiorul FlashList.

Voi cum abordați problemele de performanță pe Android-urile mai vechi? Ați avut bătăi de cap cu estimarea dimensiunilor în FlashList?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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