This episode covers efficient lists and scrolling: FlatList and SectionList with keyExtractor, getItemLayout, and windowing, onEndReached for infinite scroll, plus render optimization with memo and stable keys.

Social media feeds, product lists, and chat have one thing in common: data that can be thousands of items long. If you render all of it at once, the app will stutter because memory and the UI Thread work out of control.
Episode 8 covers how to present long lists efficiently: FlatList and SectionList with keyExtractor, getItemLayout, and windowing, onEndReached for infinite scroll, then render optimization with memo and stable keys. These are the performance foundations that will continue in episode 18.
ScrollView renders all children at once — suitable for static content with few items. For long data, use FlatList, which applies virtualization: only items near the visible area are rendered, the rest are recycled as you scroll past them.
import { FlatList, View, Text } from "react-native";
<FlatList
data={posts}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ padding: 16 }}>
<Text>{item.title}</Text>
</View>
)}
initialNumToRender={10}
windowSize={5}
/>initialNumToRender={10} controls how many initial items are rendered, and windowSize determines how many screen-heights around the visible area stay rendered. Both limit the initial work when the list opens.
keyExtractor gives each item a unique identity. Without a stable key, React struggles to track items when data changes — scroll jumps and item state gets mixed up. Use the id from the server, not the index, because the index changes when items are removed or added.
keyExtractor={(item) => item.id}If all items have a fixed height, tell FlatList via getItemLayout so scroll jumps like scroll-to-index work accurately without measuring:
const ITEM_HEIGHT = 56;
getItemLayout={(_, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
})}SectionList adds groups and headers on top of FlatList — suitable for contact lists per letter or products per category:
import { SectionList, Text } from "react-native";
<SectionList
sections={[
{ title: "A", data: ["Apel", "Alpukat"] },
{ title: "B", data: ["Belimbing"] },
]}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => <Text>{item}</Text>}
renderSectionHeader={({ section }) => <Text>{section.title}</Text>}
/>SectionList accepts the same props as FlatList: initialNumToRender, windowSize, and maxToRenderPerBatch. For thousands of items, a combination of small values for initialNumToRender and reasonable values for windowSize keeps scrolling at 60 fps.
The news feed pattern: load the next page when the user approaches the end of the list. onEndReached is called when the scroll distance from the end is less than onEndReachedThreshold (a fraction of the list length):
const [halaman, setHalaman] = useState(1);
const [posts, setPosts] = useState([]);
function muatLagi() {
setHalaman((p) => {
ambilPostingan(p + 1).then((baru) => {
setPosts((lama) => [...lama, ...baru]);
});
return p + 1;
});
}
<FlatList
data={posts}
onEndReached={muatLagi}
onEndReachedThreshold={0.5}
ListFooterComponent={() => <Text>Memuat...</Text>}
/>onEndReached can fire several times while the user holds scrolling at the end. Add a guard so requests aren't duplicated — for example a sedangMemuat flag that is only reset after the response arrives.
Every parent data change re-renders all children. Wrap list items with memo so the component only re-renders when its props change:
import React, { memo } from "react";
import { Pressable, Text } from "react-native";
export const ItemList = memo(function ItemList({ item, onPress }) {
return (
<Pressable onPress={onPress}>
<Text>{item.title}</Text>
</Pressable>
);
});memo alone isn't enough if the onPress prop is recreated every render. Stabilize it with useCallback — discussed in episode 5 — so the function reference stays the same across renders.
Don't write renderItem inline in JSX if you can avoid it. A new function every render cancels the benefit of memo. Define renderItem outside the component or wrap it with useCallback with minimal dependencies.
Warning
Don't use the index as a key for lists whose items can be inserted or removed. The index changes and React thinks items moved, causing visual state to get swapped. Use a unique id from the data or combine it with another field.
Episode 8 made long lists feel light: FlatList and SectionList with virtualization, keyExtractor and getItemLayout for accuracy, onEndReached for infinite scroll, and memo plus stable keys to reduce re-renders.
Key takeaways:
ScrollView for short content; FlatList for long data.getItemLayout speeds up scroll jumps when item height is fixed.onEndReached for infinite scroll, complete with a duplicate-request guard.memo and useCallback cut down unnecessary re-renders.In the next episode, episode 9, we'll discuss AsyncStorage and local persistence: key-value storage with AsyncStorage, SQLite and MMKV for large data, sensitive data encryption, plus offline-first patterns with a queue and sync strategies.