İçeriğe geç
Muhammet Şafak
Günlük 3 dk okuma

React Native'de performans: liste render'ı

React Native'de büyük listeleri akıcı tutmanın somut teknikleri; FlatList, memo ve keyExtractor kullanımı ile gerçek deneyimler.


Looplio’da kullanıcının takvim görünümünden tüm görevler listesine geçtiğinde ciddi bir yavaşlama vardı. Liste uzun; her öğe renk etiketi, tarih bilgisi ve tamamlanma durumu gibi birkaç bileşen içeriyor. Kaydırma sırasında takılmalar başladı. Telefon güçlüydü ama bu beni rahatlatmıyordu — daha zayıf cihazlarda daha kötü olacağını biliyordum.

Bu bir React Native performans dersi oldu.

ScrollView ile FlatList arasındaki fark

İlk hata, liste için ScrollView kullanmaktı. ScrollView tüm çocuk bileşenleri aynı anda render eder. Listenin 50 öğesi varsa, 50 öğe birden ekrana basılıyor. Kullanıcı sadece 10 tanesini görebilse de.

FlatList (ve kardeşi SectionList) virtualization yapar: yalnızca ekranda görünen ve yakında görünecek öğeleri render eder. Kullanıcı kaydırdıkça öğeler yeniden kullanılır. Büyük listeler için bu kritik bir fark.

// Kötü: tüm öğeleri aynı anda render eder
<ScrollView>
  {tasks.map((task) => (
    <TaskCard key={task.id} task={task} />
  ))}
</ScrollView>

// İyi: yalnızca görünen öğeleri render eder
<FlatList
  data={tasks}
  keyExtractor={(item) => item.id}
  renderItem={({ item }) => <TaskCard task={item} />}
/>

keyExtractor neden önemli

React’te her liste öğesinin benzersiz bir key’i olmalı; bu React’in hangi öğenin değiştiğini, hangisinin yeniden render edilmesi gerektiğini anlamasını sağlıyor.

keyExtractor prop’u FlatList’e bu anahtarı nasıl bulacağını söylüyor. item.id gibi gerçekten benzersiz bir değer kullanmak gerekiyor; index kullanmak cazip görünüyor ama listeye öğe eklenip silindiğinde yanlış güncellemelere yol açıyor.

<FlatList
  data={tasks}
  keyExtractor={(item) => item.id.toString()}
  renderItem={({ item }) => <TaskCard task={item} />}
/>

Bileşen içindeki gereksiz yeniden render’ı önlemek

FlatList virtualization yapsa da, her kaydırmada üst bileşen yeniden render olursa renderItem fonksiyonu yeniden oluşturuluyor; bu da listedeki her görünen öğenin yeniden render edilmesine neden oluyor.

İki araç burada devreye giriyor:

React.memo: Bileşeni sarıp prop’ların değişmediği durumlarda yeniden render’ı önler.

const TaskCard = React.memo(({ task }: { task: Task }) => {
  return (
    <View style={styles.card}>
      <Text style={[styles.title, task.completed && styles.completed]}>
        {task.title}
      </Text>
      <Text style={styles.date}>{formatDate(task.dueDate)}</Text>
    </View>
  );
});

useCallback: renderItem fonksiyonunu memoize eder; üst bileşen yeniden render olsa bile fonksiyon referansı değişmez.

const renderTask = useCallback(
  ({ item }: { item: Task }) => <TaskCard task={item} />,
  []
);

<FlatList
  data={tasks}
  keyExtractor={(item) => item.id.toString()}
  renderItem={renderTask}
/>;

useCallback’in dependency array’i önemli. Eğer renderTask içinde üst bileşenin bir state’ini kullanıyorsanız, onu bağımlılıklara eklemeniz gerekiyor; aksi hâlde eski değeri kullanırsınız.

getItemLayout ile kaydırma hızını artırmak

FlatList, her öğenin yüksekliğini varsayılan olarak render ettikten sonra hesaplar. Tüm öğeleriniz sabit yükseklikteyse, bu hesaplamanın önüne geçebilirsiniz:

<FlatList
  data={tasks}
  keyExtractor={(item) => item.id.toString()}
  renderItem={renderTask}
  getItemLayout={(_, index) => ({
    length: TASK_CARD_HEIGHT,
    offset: TASK_CARD_HEIGHT * index,
    index,
  })}
/>

Bu özellikle scrollToIndex kullanıyorsanız fark yaratıyor; FlatList tam pozisyonu bildiği için anında götürebiliyor.

windowSize ve initialNumToRender

FlatList’in performans ayarlarından iki önemli prop:

  • initialNumToRender: ilk render’da kaç öğe gösterilsin. Varsayılan 10’dur; uzun kartlar için düşürmek ilk açılış süresini azaltır.
  • windowSize: kaydırma penceresinin büyüklüğü. Varsayılan 21’dir (görünen alanın 10 katı üstten ve 10 katı alttan). Azaltmak bellek kullanımını düşürür ama hızlı kaydırmada beyaz alan görünebilir.

Bu değerleri körü körüne değiştirmeyin; ekran ölçüsüne ve kart içeriğine göre test ederek ayarlayın.

Looplio’da sonuç

Değişiklikleri uyguladıktan sonra — React.memo, useCallback, getItemLayout — 200 öğelik listede kaydırma akıcılaştı. Düşük güçlü bir cihazda test etmek için React Native’in Flipper entegrasyonu ve JS thread frame rate monitörü yararlı oldu.

Performans optimizasyonu kör düzeltme değil. Önce nerede yavaş olduğunu ölçün; sonra orayı düzeltin. React.memo’yu her bileşene uygulamak performans kazandırmaz; yanlış yerde kullanmak ek karşılaştırma maliyeti getirir. Ölçmek, tahmin etmekten iyidir.

Etiketler: #React Native
Paylaş:

İlgili Yazılar

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi