Twenty items in a feed feels perfect. The scroll is butter. Your client is happy. Then you ship to production, the data arrives, and suddenly you are staring at two thousand rows. The UI starts to hitch. Memory creeps up until the OS pulls the plug. In desperation, some developers wrap everything in a ScrollView and call it a day. That decision usually births three new bugs for every one it solves.

Lists are the performance bottleneck that defines how users perceive your React Native app. Get them right and the app feels native. Get them wrong and even the most beautiful screen becomes a slog. The root cause is usually a mismatch between the component you chose and the work you are asking the JavaScript and UI threads to perform. React Native runs on two tracks. Your logic lives on the JS thread while painting happens on the native UI thread. When you render a massive list incorrectly, both threads start drowning in layout calculations, re-renders, and memory allocations. The result is dropped frames, blank flashes, and eventually a crash.

Pick the Right Tool

Choosing a list component should be a deliberate architectural decision, not a reflex.

ScrollView is the simplest option. It takes every child you give it, mounts every single one into memory immediately, and hands the whole pile to the native scroll engine. That is exactly what you want for short, fixed content such as a settings screen, a login form, or a static product detail page with ten sections. It is predictable and easy to style. The catch is that there is no virtualization. If you feed it two thousand items, it will obediently create two thousand native views. Do not use ScrollView for large or dynamic data sets. Think of it as a framed poster, not a library shelf.

FlatList is the workhorse for long, uniform feeds. It virtualizes content, which means it only mounts rows that are currently visible or near the viewport. As the user scrolls, FlatList unmounts cells that leave the screen and recycles them for incoming data. This keeps memory flat regardless of how large your array grows. If you are building a social timeline, a notification center, or any continuously scrolling collection of similar cards, FlatList is the default correct choice.

SectionList is FlatList with a sense of organization. Use it when your data arrives in grouped buckets, such as an address book sorted alphabetically, a workout log split by date, or an invoice list organized by month. It renders sticky section headers and handles the grouping logic for you. Under the hood it uses the same virtualization engine as FlatList, so you get identical memory benefits with the added structure of titled partitions.

FlashList steps in when you need to squeeze every last frame out of the device. Built on top of the RecyclerListView ecosystem, it recycles views more aggressively than FlatList and aims to maintain sixty frames per second even on mid-range hardware. If you are building a high-volume chat interface, a product catalog with rapid scroll velocity, or any screen where smoothness is a competitive advantage, FlashList is worth the added dependency. It is not necessary for every screen, but for feeds that define the core experience, the performance delta is noticeable.

Common Performance Killers

There are three usual suspects when a list starts to drag.

Mounting too many React trees at once is the most dramatic failure. When every row is a complex component tree, the initial render can block the JS thread long enough to produce a blank white screen or an ugly delayed first paint. The user opens the app and waits. Even after the initial load, heavy rows make scroll initialization sluggish because the first few frames are consumed by setup work.

Too much work per frame shows up as stuttering during scroll. You have a budget of roughly sixteen milliseconds per frame to keep animations smooth. If a row component runs expensive calculations, parses dates on the fly, or performs deep object comparisons inside render, you blow that budget. The UI thread drops frames and the user feels a jolt.

Excessive memory use is the silent killer. Every native view costs RAM. Add large unoptimized images, drop shadows on every card, or nested touchables and the footprint multiplies. On iOS the system may terminate your app without warning. On Android the user watches the lag build until the app feels unusable.

Optimization Checklist

Piccole abitudini strategiche distinguono una lista che funziona semplicemente da una che vola.

Usa chiavi stabili. Passa sempre un identificatore reale dal tuo set di dati alla prop key. Non usare mai l'indice dell'array. Se la tua lista viene riordinata, filtrata o se vengono aggiunti elementi, una chiave basata sull'indice inganna React, associando i dati sbagliati al componente riciclato sbagliato. Questo errore innesca unmount non necessari, discrepanze nello stato e re-render a cascata. Un ID corretto dice a React esattamente quale riga si è spostata e dove.

Memoizza le righe. Avvolgi il tuo componente riga in React.memo in modo che si ri-renderizzi solo quando le sue prop cambiano effettivamente. Senza questa protezione, qualsiasi aggiornamento dello stato del genitore può innescare un passaggio di rendering su ogni riga visibile, anche se i loro dati sono identici. In una lista lunga e dinamica, quei cicli sprecati si accumulano rapidamente.

Mantieni stabile renderItem. Evita di definire una nuova funzione direttamente all'interno della prop renderItem ad ogni render del genitore. Una funzione arrow inline come renderItem={({ item }) => <Row data={item} />} crea un nuovo riferimento ogni volta che il genitore si aggiorna. FlatList vede una prop cambiata e ricicla la riga inutilmente. Definisci la funzione di render all'esterno del componente o memoizzala con useCallback affinché il riferimento rimanga stabile.

Usa getItemLayout quando possibile. Se le tue righe hanno un'altezza fissa o prevedibile, comunica esattamente a FlatList di cosa si tratta. Questa prop permette alla lista di saltare costose chiamate di misurazione native. Invece di misurare ogni cella dopo il mount, la lista calcola la posizione matematicamente. La differenza è particolarmente evidente nelle liste con centinaia o migliaia di elementi, dove il continuo scambio di eventi onLayout può mettere in ginocchio il thread JS.

Ottimizza le immagini in modo aggressivo. Le immagini senza dimensioni definite sono il veleno delle liste. Imposta sempre larghezza e altezza esplicite, in modo che il livello nativo riservi lo spazio prima che l'immagine venga decodificata. Per le immagini remote, usa una libreria di caching come Expo Image o un equivalente che gestisca il caching in memoria, la persistenza su disco e l'ottimizzazione del formato. Il componente Image predefinito di React Native funziona per i prototipi, ma i feed di produzione richiedono un maggiore controllo sulla memoria e sugli stati di caricamento.

Evita di annidare contenitori di scorrimento. Non inserire mai una FlatList verticale all'interno di una ScrollView verticale. La ScrollView genitore cattura tutti gli eventi di scorrimento e interrompe la capacità della FlatList figlia di misurare la propria viewport. La virtualizzazione si interrompe perché FlatList non sa più quali righe dovrebbero essere visibili. Il risultato è che ogni riga viene comunque montata, vanificando l'intero scopo della virtualizzazione. Se hai bisogno di un header sopra una lista, usa la prop ListHeaderComponent di FlatList. Se hai bisogno di un comportamento sticky complesso, usa una SectionList o FlashList con la configurazione dell'header appropriata.

La Regola d'Oro

Se il contenuto è piccolo e finito, lascia che sia ScrollView a gestirlo. Se il contenuto cresce con dati generati dagli utenti o con la paginazione remota, usa una lista virtualizzata. Quando la lista è il fulcro dell'app e gli utenti scorreranno per minuti consecutivi, punta su FlashList.

Ecco un'ultima verità che è facile dimenticare. Le righe semplici scorrono velocemente. Più il tuo componente riga è leggero, più la tua lista sarà fluida. Elimina navigazioni annidate, calcoli pesanti e animazioni superflue dalla singola riga. Mantieni il markup piatto, la logica snella e le immagini dimensionate. Una lista vive o muore in base al peso cumulativo di ciò che renderizza. Rendi ogni riga leggera, e la lista sembrerà di altissimo livello nel miglior modo possibile.