import React, { memo, useCallback } from 'react';
import { FlashList } from '@shopify/flash-list';
const ProductItem = memo(({ item, onPress }) => {
return (
<TouchableOpacity onPress={() => onPress(item.id)}>
<Text>{item.title}</Text>
</TouchableOpacity>
);
}, (prev, next) => prev.item.id === next.item.id);
const ProductList = ({ products }) => {
const handlePress = useCallback((id) => {
console.log('Pressed:', id);
}, []);
return (
<FlashList
data={products}
renderItem={({ item }) => <ProductItem item={item} onPress={handlePress} />}
estimatedItemSize={150}
/>
);
};Performanța în React Native e un subiect care dă dureri de cap oricui trece de stadiul de „Hello World”. Am lucrat recent la o aplicație de tip marketplace unde feed-ul principal trebuia să gestioneze cam 8.000 de produse, fiecare cu imagini, prețuri dinamice și butoane de wishlist. La început, scroll-ul era un coșmar: aveam cam 25-30 FPS pe un Android mid-range și se simțea oribil.
Primul instinct al multor developeri e să arunce memo, useCallback și useMemo peste tot. E o greșeală clasică. Am testat și am văzut că, dacă le pui fără discernământ, poți chiar să încetinești aplicația. De ce? Pentru că verificarea referințelor din spatele acestor hook-uri consumă și ea timp de procesare pe thread-ul de JS.
Când merită cu adevărat React.memo?
Am făcut un experiment pe componenta de „Product Card”. Fără memo, orice schimbare minoră în starea globală (cum ar fi un timer de reducere în header) declanșa re-randarea tuturor celor 10-15 carduri vizibile pe ecran. Aplicând React.memo cu o funcție de comparare custom, am redus timpul de re-render în JS thread cu aproximativ 35%.
Regula mea e simplă acum: folosesc memo doar pentru elementele de listă care sunt complexe sau care se randează de zeci de ori. Pentru un buton amărât de „Submit”, overhead-ul de a verifica props-urile e mai mare decât randarea în sine. E un trade-off sincer între mentenabilitate și viteză.
useCallback și useMemo: Nu sunt magice
useCallback e util doar într-un singur scenariu major: când pasezi o funcție către o componentă copil care este deja memorizată cu React.memo. Dacă copilul nu e memorizat, degeaba îi dai tu aceeași referință de funcție, că oricum se va randa din nou.
La useMemo, l-am folosit când aveam de filtrat un array mare de categorii înainte să le trimit către un selector. În rest, pentru calcule simple de genul „preț + TVA”, e pur și simplu inutil. Codul devine mai greu de citit și nu câștigi nimic sesizabil.
FlatList vs. FlashList: Game Changer-ul
Cea mai mare victorie am avut-o când am aruncat FlatList la gunoi și am instalat FlashList de la Shopify. FlatList are o problemă structurală: creează și distruge componente pe măsură ce faci scroll, ceea ce pune presiune pe bridge-ul de React Native și pe memorie.
FlashList folosește „recycling”. În loc să distrugă componenta care tocmai a ieșit din ecran, o păstrează și doar îi schimbă datele (props-urile) pentru a o refolosi la baza listei. Trecerea a fost aproape instantanee. Am urcat de la acei 30 FPS la un 60 FPS constant pe aproape orice dispozitiv.
Totuși, există un mare „dar”. FlashList te obligă să declari un estimatedItemSize. Dacă valoarea asta e mult diferită de realitate, o să ai un efect de „jumpy scroll” (lista sare când calculează dimensiunile reale). Eu am rezolvat-o făcând o medie pe 100 de iteme și rezultatul a fost mult peste ce putea scoate FlatList cu getItemLayout.
În concluzie, nu optimiza prematur. Folosește Profiler-ul din Flipper, vezi ce componente se randează aiurea și abia atunci scoate artileria grea. Voi ce folosiți pentru listele lungi, ați rămas pe FlatList sau ați făcut pasul spre FlashList?