0Pricing
React Native Academy · Ders

FlatList Performansını Ayarlama

Sabit yükseklikteki satırlar için getItemLayout uygulayın, initialNumToRender ve windowSize değerlerini ayarlayın, keyExtractor'ı doğru kullanın ve renderItem içinde anonim fonksiyon başvurularından kaçının.

FlatList Performansını Ayarlama, CoddyKit'te ücretsiz bir React Native Academy dersidir. Bu, 4 dersinin 3. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, React Native Academy öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. React Native Academy kursu toplamda 4 dersten oluşur.

Bu dersin bazı bölümleri henüz çevrilmemiş olup İngilizce olarak gösterilmektedir.

Why FlatList Performance Matters

FlatList is the workhorse of React Native UIs — almost every app has at least one list. A poorly configured FlatList causes dropped frames during scroll, blank cells appearing as items load, and excessive memory use that can crash the app on older devices.

FlatList is a virtualized list, meaning it only renders items currently visible on screen plus a small buffer. The challenge is configuring that buffer and the rendering pipeline so scroll feels instantaneous and does not block the JS thread.

The keyExtractor Prop

The keyExtractor prop tells FlatList how to uniquely identify each item. React uses keys for efficient reconciliation — when the list data changes, React matches new items to existing rendered components using the key, then only updates items that actually changed.

Always use a stable, unique identifier like a database ID as the key. Never use the array index — if items are added, removed, or reordered, index-based keys cause React to re-render or mis-match components.

// ❌ Index-based keys — breaks on reorder/insert
<FlatList
  data={posts}
  keyExtractor={(item, index) => String(index)}
  renderItem={renderPost}
/>

// ✅ Stable ID-based keys
<FlatList
  data={posts}
  keyExtractor={(item) => item.id}
  renderItem={renderPost}
/>

getItemLayout for Fixed-Height Rows

By default, FlatList measures the height of each rendered item to build a scroll position map. This measurement happens on the JS thread and adds overhead. If all your rows have the same fixed height, you can skip measurement entirely with getItemLayout.

Provide a function that returns the height, offset, and index for each item. The offset is height * index. This allows FlatList to calculate scroll positions instantly and also enables scrollToIndex to work correctly.

const ITEM_HEIGHT = 72;

<FlatList
  data={contacts}
  keyExtractor={(item) => item.id}
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  })}
  renderItem={({ item }) => <ContactRow contact={item} />}
/>

initialNumToRender and windowSize

initialNumToRender controls how many items are rendered on the first mount. Set it to the number of items visible on the screen without scrolling — rendering more wastes the initial render time. The default is 10, which is often too high or too low depending on your row height.

windowSize controls the size of the render window as a multiple of the visible viewport. A windowSize of 5 means FlatList renders 2.5 viewport-heights above and below the visible area. Lower values reduce memory; higher values reduce blank cell flashes when scrolling fast.

<FlatList
  data={posts}
  keyExtractor={(item) => item.id}
  renderItem={renderItem}
  initialNumToRender={8}     // Render 8 items on first paint
  windowSize={5}             // Render 2.5x viewport above/below
  maxToRenderPerBatch={5}    // Render up to 5 new items per JS frame
  updateCellsBatchingPeriod={50} // Check for updates every 50ms
/>

Avoiding Anonymous renderItem Functions

Defining renderItem as an inline arrow function inside JSX creates a new function reference on every parent render. This defeats the reconciliation optimization in FlatList and also defeats any React.memo you applied to the row component.

Define renderItem as a named function outside the component body (for pure renderers) or with useCallback inside the component when it needs to close over state or callbacks. The same applies to ItemSeparatorComponent and ListEmptyComponent.

// ❌ New reference on every render
return (
  <FlatList
    data={posts}
    renderItem={({ item }) => <PostRow post={item} onPress={handlePress} />}
  />
);

// ✅ Stable reference with useCallback
const renderItem = useCallback(
  ({ item }) => <PostRow post={item} onPress={handlePress} />,
  [handlePress]
);

return <FlatList data={posts} renderItem={renderItem} />;

removeClippedSubviews for Large Lists

Setting removeClippedSubviews={true} tells the native layer to detach views that are far outside the viewport from the native view hierarchy, freeing GPU memory. This is particularly effective on Android where keeping hundreds of off-screen views attached can degrade scroll performance.

Enable this option when your list has more than 50-100 items. There is a minor caveat on iOS: removed views may flash on very fast scrolling. If you see this issue, lower your windowSize instead.

<FlatList
  data={items}
  keyExtractor={(item) => item.id}
  renderItem={renderItem}
  removeClippedSubviews={true} // Detach off-screen views from native layer
  windowSize={5}
/>

Avoiding State in renderItem

Each call to renderItem should be a pure function of the item data and any stable callbacks. Avoid reading component state or context values inside renderItem unless they are stable references — every state change will re-render the entire list.

If each row needs dynamic data (like whether the current user has liked a post), include that data in the item object itself or pass it as a prop from a stable selector. This keeps FlatList's reconciliation efficient.

// ❌ Reading volatile state inside renderItem
const renderItem = ({ item }) => (
  <PostRow post={item} isLiked={likedPostIds.includes(item.id)} />
);
// If likedPostIds is in state, all rows re-render on every like

// ✅ Merge liked status into the data array before passing to FlatList
const enrichedPosts = useMemo(
  () => posts.map((p) => ({ ...p, isLiked: likedPostIds.has(p.id) })),
  [posts, likedPostIds]
);

<FlatList data={enrichedPosts} renderItem={({ item }) => (
  <PostRow post={item} />
)} />

Pull-to-Refresh Performance

The onRefresh and refreshing props add a pull-to-refresh control. Set refreshing to true while your data fetch is in progress, and back to false when it completes. This shows the native RefreshControl spinner.

For performance, avoid setState calls that cause unnecessary FlatList updates during the refresh. Only update the data when the fetch is complete so FlatList does a single reconciliation pass, not multiple intermediate ones.

const [refreshing, setRefreshing] = useState(false);

async function handleRefresh() {
  setRefreshing(true);
  try {
    const fresh = await fetchPosts();
    setPosts(fresh); // Single state update
  } finally {
    setRefreshing(false);
  }
}

<FlatList
  data={posts}
  renderItem={renderItem}
  refreshing={refreshing}
  onRefresh={handleRefresh}
/>

Measuring List Performance with InteractionManager

InteractionManager defers expensive work until after pending animations and interactions complete. This is useful for loading initial screen content: animate the screen transition first, then fetch and render heavy list data after the animation finishes.

Wrap expensive initial data loads in InteractionManager.runAfterInteractions to keep screen transitions at 60fps. Without this, a heavy first fetch can block the JS thread during the transition, causing a janky slide-in animation.

import { InteractionManager } from 'react-native';

useEffect(() => {
  // Defer data load until screen transition completes
  const task = InteractionManager.runAfterInteractions(async () => {
    const data = await fetchLargeDataset();
    setPosts(data);
  });

  return () => task.cancel();
}, []);

FlashList: A Faster Alternative

FlashList by Shopify is a drop-in replacement for FlatList that is significantly faster for large lists. It recycles native cell components (similar to RecyclerView on Android) instead of creating new ones, which reduces memory allocation and garbage collection pressure.

Replace FlatList with FlashList and provide the estimatedItemSize prop instead of getItemLayout. Most other FlatList props are compatible. FlashList is the recommended choice for lists with more than 100 items.

import { FlashList } from '@shopify/flash-list';

// Install: npx expo install @shopify/flash-list

<FlashList
  data={posts}
  keyExtractor={(item) => item.id}
  estimatedItemSize={72}   // Average row height in pixels
  renderItem={renderItem}
  onEndReached={loadMore}
  onEndReachedThreshold={0.5}
/>

Profiling FlatList with the JS Thread Monitor

After tuning your FlatList, verify the improvement with the Performance Monitor (developer menu). While scrolling, watch the JS FPS counter. If it stays near 60fps, your list is rendering efficiently. A sustained drop below 50fps indicates the JS thread is overloaded during scroll.

The most common remaining bottleneck after applying the above optimizations is image loading. Large remote images decode on the main thread. Use FastImage (react-native-fast-image) to add caching and progressive loading, which prevents image decode from blocking scroll frames.

Quick Check

Test your understanding of React Native Mobile Development concepts from this lesson.

Lesson Recap

In this lesson you learned: how getItemLayout eliminates runtime height measurements for fixed-height rows, how initialNumToRender and windowSize control how many items are rendered around the viewport, and why stable renderItem references are critical for FlatList reconciliation efficiency. Next up we analyze the JavaScript bundle size and apply lazy loading to reduce app startup time.

Sıkça Sorulan Sorular

“FlatList Performansını Ayarlama” dersi ücretsiz mi?

Evet — “FlatList Performansını Ayarlama” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve React Native Academy kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. React Native Academy kursu toplamda 4 dersten oluşur.

“FlatList Performansını Ayarlama” dersinde ne öğreneceğim?

Sabit yükseklikteki satırlar için getItemLayout uygulayın, initialNumToRender ve windowSize değerlerini ayarlayın, keyExtractor'ı doğru kullanın ve renderItem içinde anonim fonksiyon başvurularından… React Native Academy ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

React Native Academy öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te React Native Academy, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 3. dersidir.

“FlatList Performansını Ayarlama” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu React Native Academy dersinde kod yazıp çalıştırabilir miyim?

Evet. Her React Native Academy dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. Flipper ve React DevTools ile Profil Oluşturma
  2. React.memo, useCallback ve useMemo ile Anımsama
  3. FlatList Performansını Ayarlama
  4. Paket Boyutu ve Tembel Yükleme
← React Native Academy Sayfasına Dön