Displaying Lists Performantly with FlatList
Displaying Lists with FlatList
~9 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
Now let's bring it all together: display the products we loaded in the last chapter as a scrollable list of ProductCard components. In HTML you'd build a <ul> with a .map() loop over <li>. React Native has a dedicated, performance-optimized component for this: FlatList.
Why not just .map() inside a View?
Technically you could write {{products.map(p => <ProductCard key={{p.id}} ... />)}} inside a regular View – with 10 products that even works fine. With 500 products, though, it becomes a performance problem: ALL 500 cards would be created immediately and kept in memory, even the ones that aren't visible at all. FlatList, by contrast, only renders the items currently (or soon to be) visible on screen, and recycles memory as you scroll – comparable to "virtualized scrolling", which you may know from web libraries like react-window.
Updating App.js: FlatList instead of a counter
Let's replace App.js once more – the ProductCard import is back, and instead of just showing the product count, we now render the entire list:
import { useState, useEffect } from 'react';
import { View, Text, FlatList, StyleSheet, ActivityIndicator } from 'react-native';
import { fetchProducts } from './api/magentoApi';
import ProductCard from './components/ProductCard';
export default function App() {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function loadProducts() {
try {
const items = await fetchProducts();
setProducts(items);
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
}
loadProducts();
}, []);
if (loading) {
return <ActivityIndicator size="large" style={styles.loader} />;
}
return (
<View style={styles.container}>
<FlatList
data={products}
keyExtractor={(item) => item.sku}
renderItem={({ item }) => (
<ProductCard
name={item.name}
price={item.price}
imageUrl={item.imageUrl}
onPress={() => console.log('Tapped:', item.name)}
/>
)}
ListEmptyComponent={<Text>No products found.</Text>}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, paddingTop: 16, paddingHorizontal: 16 },
loader: { flex: 1, justifyContent: 'center' },
});Save and check your phone: you now see your shop's real, scrollable product list, each row a ProductCard with its own favorite heart.
The three most important props, compared to HTML
| HTML/Web | React Native |
|---|---|
<ul>{{items.map(i => <li key={{i.id}}>...</li>)}}</ul> | data – the array of raw data (corresponds to items) |
key={{i.id}} inside the .map() loop | keyExtractor – a function that turns each item into a unique string key (for us, the product SKU) |
The JSX expression inside the .map() loop | renderItem – a function that turns an item into the component to display |
keyExtractor matters so React knows exactly which list item corresponds to which data object when redrawing – exactly like the key attribute on .map() loops in regular React for the web. Without it, FlatList falls back to the index by default, which can cause display glitches when sorting or filtering changes.
More useful props (preview)
ListEmptyComponent– we just used this above, for the "no products found" caseListHeaderComponent/ListFooterComponent– elements before/after the actual list, e.g. a heading or a "Load more" buttononRefresh+refreshing– enables the "pull-to-refresh" gesture users know from practically every mobile apphorizontal– flips the scroll direction from vertical to horizontal, useful for product carousels
Tipp: We deliberately don't wire up onRefresh/refreshing here to keep the chapter compact – you can easily add them later: onRefresh simply calls loadProducts() again, refreshing binds to the existing loading state.