react-window ile Tanışmak: Büyük Listeleri Verimli Bir Şekilde Oluşturma Kılavuzu
Modern web uygulamaları, kullanıcılarına zengin ve dinamik arayüzler sunarken, genellikle büyük veri kümeleriyle çalışmak zorunda kalır. Bu veri kümeleri, özellikle listeler veya tablolar halinde sunulduğunda, uygulama performansını ciddi şekilde etkileyebilir. Binlerce, hatta on binlerce öğeden oluşan bir listeyi doğrudan DOM’a (Document Object Model) render etmek, tarayıcının kaynaklarını aşırı tüketerek yavaşlamaya, donmalara ve kötü bir kullanıcı deneyimine yol açabilir. İşte tam bu noktada sanalizasyon (virtualization) veya pencereleme (windowing) teknikleri devreye girer ve react-window bu alandaki en popüler ve etkili kütüphanelerden biridir.
Bu kapsamlı makalede, react-window‘un ne olduğunu, neden bu kadar önemli olduğunu, nasıl çalıştığını ve uygulamalarınızda büyük listeleri verimli bir şekilde yönetmek için nasıl kullanabileceğinizi derinlemesine inceleyeceğiz. Temel kullanımından gelişmiş senaryolara, optimizasyon tekniklerinden birlikte kullanılabilecek diğer kütüphanelere kadar her yönüyle react-window‘u ele alacağız.
Neden react-window’a İhtiyaç Duyarız?
Web uygulamalarında performans, kullanıcı memnuniyetinin temel taşlarından biridir. Özellikle tek sayfa uygulamaları (SPA’lar) ve veri yoğun arayüzler geliştirirken, performans darboğazları kaçınılmaz hale gelebilir. Büyük listeler, bu darboğazların en yaygın kaynaklarından biridir. Peki, neden büyük listeler performansı olumsuz etkiler?
Bir React uygulamasında, her bir liste öğesi genellikle kendi bileşenini temsil eder ve bu bileşenler DOM’a render edilir. 10.000 öğelik bir liste düşünün:
* DOM Manipülasyonu: Tarayıcının 10.000’den fazla DOM düğümü oluşturması, güncellemesi ve yönetmesi gerekir. Bu işlemler, özellikle her kaydırma veya veri güncellemesinde, önemli CPU zamanı ve bellek tüketimi anlamına gelir.
* Bellek Tüketimi: Her bir DOM düğümü ve onunla ilişkili JavaScript nesneleri (olay dinleyicileri, stil bilgileri vb.) bellekte yer kaplar. Büyük listeler, yüzlerce megabayt belleği kolayca tüketebilir, bu da özellikle mobil cihazlarda veya düşük özellikli makinelerde uygulamaların çökmesine neden olabilir.
* Layout ve Paint İşlemleri: Tarayıcının, tüm bu öğelerin konumunu ve stilini hesaplaması (layout) ve ardından ekrana çizmesi (paint) gerekir. Bu işlemler, liste büyüdükçe katlanarak artar ve akıcı kaydırma deneyimini engeller.
* JavaScript Yürütme: React’in sanal DOM farkı alma (diffing) ve uzlaştırma (reconciliation) süreçleri bile büyük listelerde yavaşlayabilir, çünkü tüm öğeler için prop karşılaştırmaları yapılması gerekir.
Bu sorunları çözmek için sanalizasyon adı verilen bir teknik kullanılır. react-window, React ekosisteminde bu tekniği uygulayan, hafif ve hızlı bir kütüphanedir.
Sanalizasyon (Virtualization) Nedir ve Nasıl Çalışır?
Sanalizasyon, “pencereleme” olarak da bilinir, büyük listelerin performans sorunlarını çözmek için kullanılan temel bir optimizasyon tekniğidir. Bu tekniğin arkasındaki ana fikir basittir: Sadece o an kullanıcının ekranında görünen (veya çok az bir tampon bölgeyle görünür hale gelmek üzere olan) öğeleri render et. Geri kalan öğeler DOM’a eklenmez, dolayısıyla tarayıcının iş yükü büyük ölçüde azalır.
Sanalizasyonun çalışma prensibi adımlar halinde şöyle özetlenebilir:
1. Görünüm Alanının Tespiti: Kütüphane, listenin içinde bulunduğu kapsayıcı elemanın (viewport) boyutunu ve kaydırma pozisyonunu sürekli olarak izler.
2. Görünür Öğelerin Hesaplanması: Bu bilgilere dayanarak, hangi liste öğelerinin görünüm alanına girdiğini veya girmek üzere olduğunu (genellikle bir “overscan” veya tampon bölge ile) hesaplar.
3. Dinamik Render: Sadece bu hesaplanan görünür öğeler DOM’a eklenir ve render edilir. Görünüm alanı dışındaki öğeler DOM’dan kaldırılır.
4. Boşluk Bırakma (Placeholder): Görünür olmayan öğelerin kaplayacağı toplam alanı korumak için, listenin başında ve sonunda uygun yükseklikte (dikey listeler için) veya genişlikte (yatay listeler için) boş DOM elemanları (genellikle bir div) oluşturulur. Bu, kaydırma çubuğunun doğru boyutta olmasını ve kullanıcının listenin tamamı üzerinde kaydırıyormuş gibi hissetmesini sağlar.
5. Öğe Yeniden Kullanımı: Kullanıcı kaydırdığında, görünüm alanından çıkan öğeler DOM’dan tamamen kaldırılmak yerine, yeni görünüm alanına giren öğeler için yeniden kullanılır. Bu, DOM manipülasyonunun maliyetini daha da azaltır.
react-window, bu mekanizmayı React bileşenleri dünyasına taşır. Kendisinden önce gelen react-virtualized‘a göre daha küçük bir boyuta ve daha basit bir API’ye sahiptir, çünkü sadece listeleme ve ızgara (grid) sanalizasyonuna odaklanmıştır.
react-window’un Temelleri: Kurulum ve İlk Adımlar
react-window‘u projenize dahil etmek oldukça basittir. Öncelikle, kütüphaneyi projenize kurmanız gerekir:
npm install react-window
veya
yarn add react-window
Kurulum tamamlandıktan sonra, react-window‘un temel bileşenlerini kullanmaya başlayabilirsiniz. Kütüphane iki ana liste bileşeni sunar:
* FixedSizeList: Tüm öğelerin sabit bir yüksekliğe (dikey listeler için) veya genişliğe (yatay listeler için) sahip olduğu durumlar için.
* VariableSizeList: Öğelerin değişken yüksekliğe veya genişliğe sahip olduğu durumlar için.
Her iki bileşen de benzer temel prop’ları paylaşır:
* height: Listenin görünen yüksekliği (piksel cinsinden).
* width: Listenin görünen genişliği (piksel cinsinden).
* itemCount: Listedeki toplam öğe sayısı.
* children: Her bir liste öğesini render edecek bir fonksiyon (render prop). Bu fonksiyona index (öğenin dizini) ve style (öğenin konumunu belirleyen stil nesnesi) prop’ları iletilir.
* itemData (isteğe bağlı): Liste öğelerine aktarılacak herhangi bir ek veri. Performans optimizasyonu için önemlidir.
Şimdi basit bir FixedSizeList örneği ile başlayalım:
import React from 'react';
import { FixedSizeList } from 'react-window';
const Row = ({ index, style }) => (
Liste Öğesi {index}
);
const MyFixedSizeList = () => (
{Row}
);
export default MyFixedSizeList;
Bu örnekte:
* FixedSizeList bileşeni, 400 piksel yüksekliğinde ve 600 piksel genişliğinde bir alanı kaplar.
* itemCount={1000} ile 1000 adet liste öğesi olduğunu belirtiriz.
* itemSize={50} ile her bir öğenin yüksekliğinin 50 piksel olduğunu tanımlarız.
* Row bileşeni, FixedSizeList‘in children prop’una geçilen bir render fonksiyonudur. react-window bu fonksiyona index (hangi öğenin render edildiğini belirtir) ve style (öğenin DOM’daki doğru konumunu ve boyutunu ayarlayan stil) prop’larını iletir. Bu style prop’unu kesinlikle öğenize uygulamalısınız, aksi takdirde sanalizasyon çalışmaz!
Bu kurulum ile, sadece birkaç öğe DOM’a render edilirken, kullanıcı 1000 öğelik bir listede sorunsuz bir şekilde kaydırma yapabilir.
FixedSizeList: Sabit Boyutlu Öğeler İçin
FixedSizeList, adından da anlaşılacağı gibi, listedeki tüm öğelerin aynı yüksekliğe (dikey listeler için) veya genişliğe (yatay listeler için) sahip olduğu senaryolar için tasarlanmıştır. Bu, react-window‘un en basit ve en performanslı kullanım şeklidir, çünkü kütüphane her bir öğenin konumunu ve boyutunu kolayca hesaplayabilir.
Kullanım Senaryoları:
* Veri Tabloları: Tüm satırların aynı yüksekliğe sahip olduğu tablolar.
* Ürün Listeleri: Her ürün kartının sabit bir boyuta sahip olduğu e-ticaret sitelerindeki listeler.
* Ayarlar Menüleri: Sabit yükseklikteki menü öğeleri.
* Basit Mesaj Listeleri: Tüm mesajların aynı yüksekliğe sahip olduğu sohbet uygulamaları.
Önemli Prop:
* itemSize: Bu prop, bir sayı değeri alır ve her bir öğenin piksel cinsinden boyutunu (dikey listeler için yükseklik, yatay listeler için genişlik) belirtir. Bu değer, react-window‘un sanal kaydırma mekanizmasını doğru bir şekilde oluşturması için kritik öneme sahiptir.
Örnek Kod: Yatay Liste
FixedSizeList varsayılan olarak dikey listeler oluşturur. Yatay bir liste oluşturmak için layout="horizontal" prop’unu kullanmanız yeterlidir. Bu durumda itemSize prop’u öğelerin genişliğini belirleyecektir.
import React from 'react';
import { FixedSizeList } from 'react-window';
const Column = ({ index, style }) => (
Sütun {index}
);
const MyFixedSizeHorizontalList = () => (
{Column}
);
export default MyFixedSizeHorizontalList;
Bu örnekte, height prop’u listenin yüksekliğini (bu durumda sabittir), width prop’u ise yatay olarak ne kadar alan kaplayacağını belirtir. itemSize bu sefer her bir sütunun genişliğini temsil eder.
FixedSizeGrid:
react-window ayrıca sabit boyutlu öğelerden oluşan ızgaralar (grid) için FixedSizeGrid bileşenini sunar. Bu bileşen, hem satır hem de sütun boyutlarının sabit olduğu durumlarda kullanılır ve columnCount, columnWidth, rowCount, rowHeight gibi ek prop’lar alır. Kullanım mantığı FixedSizeList‘e benzerdir ancak iki boyutludur.
VariableSizeList: Değişken Boyutlu Öğeler İçin
Gerçek dünya uygulamalarında, listelerdeki tüm öğelerin aynı boyutta olması nadirdir. Örneğin, bir sohbet uygulamasındaki mesajlar farklı uzunluklarda olabilir, bir haber akışındaki makaleler farklı resimler ve metin içerikleri nedeniyle farklı yüksekliklere sahip olabilir. Bu gibi durumlarda FixedSizeList yetersiz kalır, çünkü her öğenin boyutunu önceden bilmez ve sabit bir itemSize değeri atayamayız. İşte burada VariableSizeList devreye girer.
VariableSizeList, listedeki öğelerin farklı boyutlara sahip olabileceği senaryolar için tasarlanmıştır. Bu bileşen, itemSize prop’unu bir sayı yerine bir fonksiyon olarak alır.
itemSize Prop’u Fonksiyon Olarak:
VariableSizeList için itemSize prop’u, index parametresini alan ve o dizindeki öğenin piksel cinsinden boyutunu döndüren bir fonksiyon olmalıdır:
import React from 'react';
import { VariableSizeList } from 'react-window';
// Örnek olarak, her öğenin yüksekliği rastgele olsun
const getItemSize = index => (index % 3 === 0 ? 75 : 50); // Bazı öğeler 75px, bazıları 50px
const Row = ({ index, style }) => (
Değişken Boyutlu Öğesi {index} (Yükseklik: {getItemSize(index)}px)
);
const MyVariableSizeList = () => (
{Row}
);
export default MyVariableSizeList;
Bu örnekte, getItemSize fonksiyonu her bir öğenin dizinine göre farklı bir yükseklik döndürür. react-window, her öğenin konumunu ve boyutunu dinamik olarak hesaplamak için bu fonksiyonu kullanır.
Performans Etkileri ve Optimizasyon:
itemSize fonksiyonunun her render’da veya her kaydırma olayında çağrılması, özellikle çok büyük listelerde performans sorunlarına yol açabilir. Eğer öğe boyutları değişirse veya listenin içeriği güncellenirse, react-window‘un bu boyutları yeniden hesaplaması gerekir.
* Önbellekleme (Caching): Genellikle, getItemSize fonksiyonunun döndürdüğü değerler önbelleğe alınır. Eğer öğe boyutları dinamik olarak değişmiyorsa (yani, bir kez hesaplandıktan sonra sabit kalıyorsa), bu değerleri bir dizi veya harita içinde saklamak ve sadece gerektiğinde güncellemek performansı artırır.
* resetAfterIndex Metodu: VariableSizeList‘in bir resetAfterIndex metodu vardır. Bu metot, listenin boyutlarının belirli bir dizinden sonra geçersiz olduğunu ve yeniden hesaplanması gerektiğini belirtmek için kullanılır. Örneğin, bir öğenin içeriği değiştiğinde ve yüksekliği farklılaştığında, bu metodu çağırarak kütüphaneyi bilgilendirebilirsiniz. Bu metot, VariableSizeList bileşeninin bir ref‘i aracılığıyla çağrılır.
import React, { useRef, useCallback, useState } from 'react';
import { VariableSizeList } from 'react-window';
// Her öğenin yüksekliğini dinamik olarak belirleyen bir fonksiyon
const itemHeights = new Array(1000)
.fill(true)
.map(() => 25 + Math.round(Math.random() * 50)); // Rastgele yükseklikler
const getItemSize = index => itemHeights[index];
const Row = ({ index, style }) => (
Öğe {index} (Yükseklik: {getItemSize(index)}px)
);
const MyVariableSizeListWithReset = () => {
const listRef = useRef();
const [dataVersion, setDataVersion] = useState(0); // Veri güncellendiğinde tetiklemek için
const handleUpdateItemSize = useCallback((indexToUpdate) => {
// Belirli bir öğenin yüksekliğini güncelleyelim
itemHeights[indexToUpdate] = 100; // Örnek olarak 100px yapalım
// listRef.current'ın var olduğundan emin olun
if (listRef.current) {
listRef.current.resetAfterIndex(indexToUpdate); // Bu indeksten sonrasını yeniden hesapla
setDataVersion(prev => prev + 1); // Component'i yeniden render etmeye zorla (opsiyonel)
}
}, []);
return (
{Row}
);
};
export default MyVariableSizeListWithReset;
Bu örnekte, itemHeights dizisi ile her öğenin yüksekliğini önbelleğe alıyoruz. handleUpdateItemSize fonksiyonu, belirli bir öğenin yüksekliğini değiştirdiğinde listRef.current.resetAfterIndex(indexToUpdate) çağrısı yaparak react-window‘u bilgilendirir. Bu, kütüphanenin sadece etkilenen bölgedeki öğelerin boyutlarını yeniden hesaplamasını sağlar, tüm listeyi değil.
Gelişmiş Kullanım Senaryoları ve Optimizasyonlar
react-window, temel listeleme yeteneklerinin ötesinde, daha karmaşık senaryolar ve performans optimizasyonları için çeşitli araçlar sunar.
Custom Scrollbar’lar ve Scroll Pozisyonu Kontrolü
react-window tarafından oluşturulan liste, tarayıcının doğal kaydırma çubuğunu kullanır. Ancak bazen özel bir kaydırma çubuğu tasarımına veya kaydırma pozisyonunu programatik olarak kontrol etme ihtiyacına sahip olabilirsiniz.
* outerRef ve innerRef: FixedSizeList ve VariableSizeList bileşenleri, liste kapsayıcılarına referans almanızı sağlayan outerRef ve innerRef prop’ları sağlar. outerRef, listenin kaydırma yapabilen dış kapsayıcısına (viewport), innerRef ise tüm liste öğelerini içeren iç kapsayıcısına erişim sağlar. Bu referanslar aracılığıyla DOM elemanlarına doğrudan erişerek kaydırma olaylarını dinleyebilir veya özel kaydırma çubukları entegre edebilirsiniz.
* scrollToItem ve scrollTo Metotları: listRef aracılığıyla erişebileceğiniz scrollToItem(index, align) metodu, belirli bir indeksteki öğeyi görünüm alanına getirir. align parametresi ("auto", "smart", "center", "end", "start") öğenin nasıl hizalanacağını belirler. scrollTo(scrollOffset) metodu ise listeyi belirli bir kaydırma ofsetine taşır.
import React, { useRef } from 'react';
import { FixedSizeList } from 'react-window';
const Row = ({ index, style }) => (
Öğe {index}
);
const ScrollControlList = () => {
const listRef = useRef();
const scrollToMiddle = () => {
if (listRef.current) {
listRef.current.scrollToItem(500, 'center'); // 500. öğeyi ortaya hizala
}
};
const scrollToTop = () => {
if (listRef.current) {
listRef.current.scrollTo(0); // En başa kaydır
}
};
return (
{Row}
);
};
export default ScrollControlList;
Veri Yönetimi ve itemData Prop’u
Liste öğelerinize veri aktarırken, itemData prop’unu kullanmak önemlidir. Render prop’unuz (yani Row bileşeniniz) genellikle kapatma (closure) ile listeyi çevreleyen bileşenin kapsamındaki verilere erişebilir. Ancak, bu durum gereksiz yeniden render’lara yol açabilir.
Eğer listeyi çevreleyen bileşen her render edildiğinde yeni bir data dizisi oluşturulursa, render prop’u da her seferinde yeniden oluşturulur. React, bu yeni fonksiyon referansını algıladığında, FixedSizeList veya VariableSizeList‘in içindeki tüm öğeleri yeniden render etmeye zorlayabilir, bu da performansı düşürür.
itemData prop’u, listenin her bir öğesine değişmeyecek bir referansla veri aktarmanın güvenli bir yoludur. itemData‘ya aktardığınız değer, render prop’unuza itemData adında bir prop olarak iletilir. Eğer itemData referansı değişmezse, react-window‘un iç optimizasyonları sayesinde alt öğeler gereksiz yere yeniden render edilmez.
import React from 'react';
import { FixedSizeList } from 'react-window';
const allItems = Array.from({ length: 1000 }, (_, i) => ({
id: i,
name: Kullanıcı ${i},
description: Bu, ${i}. kullanıcının açıklamasıdır.,
}));
// itemData prop'unu alan bir render prop
const Item = ({ index, style, data }) => {
const item = data[index]; // itemData'dan verilere erişim
return (
{item.name} - {item.description}
);
};
const MyListWithItemData = () => (
{Item}
);
export default MyListWithItemData;
Bu yaklaşım, allItems dizisi değişmediği sürece FixedSizeList‘in ve Item bileşenlerinin gereksiz yere yeniden render edilmesini engeller.
Liste İçindeki Öğelerin Yeniden Render Edilmesi
react-window‘un kendisi, sadece görünür öğeleri render ederek ve öğe yeniden kullanımı yaparak önemli optimizasyonlar sağlar. Ancak, render prop’unuzun içinde oluşturduğunuz bireysel liste öğelerinin (örneğin, Row veya Item bileşenleri) de kendi içlerinde optimize edilmesi gerekebilir.
Eğer bir öğe bileşeniniz karmaşık mantık içeriyor ve prop’ları değişmediği halde yeniden render ediliyorsa, React.memo kullanarak bu öğeleri sarmalayabilirsiniz. Bu, öğenin prop’ları değişmediği sürece yeniden render edilmesini engeller.
import React from 'react';
import { FixedSizeList } from 'react-window';
const allItems = Array.from({ length: 1000 }, (_, i) => Öğe ${i});
// React.memo ile sarmalanmış optimize edilmiş öğe bileşeni
const MemoizedRow = React.memo(({ index, style, data }) => {
const item = data[index];
return (
{item}
);
});
const OptimizedList = () => (
{MemoizedRow} {/ Memoized bileşeni kullan /}
);
export default OptimizedList;
Yükleme Durumları (Loading States) ve Sonsuz Kaydırma (Infinite Scroll)
Büyük listeler genellikle tüm veriyi bir kerede yüklemez. Bunun yerine, kullanıcı kaydırdıkça daha fazla veri yüklenen “sonsuz kaydırma” desenini kullanırlar. react-window bu deseni doğrudan sağlamasa da, kolayca entegre edilebilir.
* Yükleme Durumu Gösterimi: Listenin sonuna yaklaşıldığında, itemCount değerini geçici olarak artırarak bir “yükleniyor” veya “daha fazla yükle” göstergesi ekleyebilirsiniz.
* Veri Yükleme Mantığı: onScroll prop’unu kullanarak kaydırma olaylarını dinleyebilir ve kullanıcının listenin sonuna yaklaşıp yaklaşmadığını kontrol edebilirsiniz. Yaklaştığında, bir API çağrısı yaparak daha fazla veri yükleyebilirsiniz.
import React, { useState, useEffect, useCallback } from 'react';
import { FixedSizeList } from 'react-window';
const LOADING = 1;
const LOADED = 2;
const itemStatusMap = {}; // Her öğenin yüklenme durumunu tutmak için
const isItemLoaded = index => !!itemStatusMap[index];
const loadMoreItems = (startIndex, stopIndex) => {
for (let i = startIndex; i <= stopIndex; i++) {
itemStatusMap[i] = LOADING;
}
return new Promise(resolve =>
setTimeout(() => {
for (let i = startIndex; i <= stopIndex; i++) {
itemStatusMap[i] = LOADED;
}
resolve();
}, 1000)
);
};
const Row = ({ index, style }) => {
let content;
if (!isItemLoaded(index)) {
content = 'Yükleniyor...';
} else {
content = Öğe ${index};
}
return (
{content}
);
};
const InfiniteScrollList = () => {
const [itemCount, setItemCount] = useState(0); // Başlangıçta 0 öğe
const [loading, setLoading] = useState(false);
useEffect(() => {
// İlk yükleme
setLoading(true);
loadMoreItems(0, 19).then(() => {
setItemCount(20);
setLoading(false);
});
}, []);
const handleScroll = useCallback(({ scrollOffset, scrollHeight, clientHeight }) => {
// Listenin sonuna yakın mı?
const threshold = 100; // Son 100 piksel kala yüklemeye başla
if (!loading && scrollOffset + clientHeight >= scrollHeight - threshold && itemCount < 1000) {
setLoading(true);
const startIndex = itemCount;
const stopIndex = Math.min(itemCount + 19, 999); // Bir sonraki 20 öğeyi yükle
loadMoreItems(startIndex, stopIndex).then(() => {
setItemCount(prevCount => prevCount + (stopIndex - startIndex + 1));
setLoading(false);
});
}
}, [loading, itemCount]);
return (
{Row}
);
};
export default InfiniteScrollList;
Bu örnek biraz basitleştirilmiş olsa da, onScroll ile kaydırma pozisyonunu izleyerek ve itemCount‘u dinamik olarak güncelleyerek sonsuz kaydırma mantığını nasıl uygulayabileceğinizi gösterir. Yükleme durumunu belirtmek için itemCount‘a geçici olarak bir öğe daha eklenir ve bu öğe “Yükleniyor…” mesajını gösterir.
Dinamik Liste Boyutları (Responsive Lists)
react-window‘un height ve width prop’ları sabit değerler bekler. Ancak, listenizin kapsayıcı elemanının boyutunun değiştiği (örneğin, pencere yeniden boyutlandırıldığında) duyarlı (responsive) bir tasarımınız varsa, bu değerleri dinamik olarak ayarlamanız gerekir.
Bu durumu yönetmek için genellikle react-window-autosizer gibi yardımcı kütüphaneler veya tarayıcının ResizeObserver API’si kullanılır.
* react-window-autosizer: Bu küçük kütüphane, bir React bileşeninin kapsayıcı elemanının boyutunu otomatik olarak algılar ve bu boyutları width ve height prop’ları olarak react-window listesine iletir.
import React from 'react';
import { FixedSizeList } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer'; // react-window-autosizer yerine react-virtualized-auto-sizer daha yaygın kullanılıyor
const Row = ({ index, style }) => (
Öğe {index}
);
const ResponsiveList = () => (
{/ Kapsayıcıya boyut verin /}
{({ height, width }) => (
{Row}
)}
);
export default ResponsiveList;
AutoSizer bileşeni, FixedSizeList‘i çevreler ve render prop’u aracılığıyla kendisine ayrılan alanı height ve width olarak iletir. Bu, listenizin kapsayıcısının boyutuna göre otomatik olarak ayarlanmasını sağlar.
react-window ile Birlikte Kullanılabilecek Kütüphaneler
react-window tek başına güçlü olsa da, ek işlevsellik veya entegrasyonlar için bazı yardımcı kütüphanelerle birlikte kullanılması yaygındır:
* react-virtualized-auto-sizer: Yukarıda bahsedildiği gibi, listenin boyutunu kapsayıcı elemanına göre otomatik olarak ayarlamak için kullanılır. react-window‘un kendi react-window-autosizer paketi de mevcut olsa da, react-virtualized‘ın daha olgun AutoSizer‘ı genellikle tercih edilir.
* react-infinite-scroll-component: Sonsuz kaydırma işlevselliğini daha kolay entegre etmek için kullanılabilir. react-window‘un onScroll prop’u ile manuel olarak yönetmek yerine, bu kütüphane daha soyut bir API sunar. Ancak, react-window‘un sanalizasyon mekanizması ile tam uyumluluk için dikkatli entegrasyon gerektirir.
* react-intersection-observer: Belirli öğelerin (örneğin, listenin sonundaki bir yükleme göstergesi) görünüm alanına girip girmediğini tespit etmek için kullanılabilir. Bu, sonsuz kaydırma tetikleyicilerini yönetmek için onScroll‘dan daha modern ve performanslı bir alternatif olabilir.
react-window’un Sınırlılıkları ve Alternatifler
react-window hafifliği ve performansı ile öne çıksa da, her senaryo için mükemmel bir çözüm değildir. Bazı sınırlamaları vardır:
* Tek Boyutlu Sanalizasyon: react-window temel olarak dikey veya yatay listeler ve basit ızgaralar için tasarlanmıştır. Karmaşık, çok boyutlu veya dinamik olarak yerleştirilmiş öğeler içeren (örneğin, Pinterest tarzı “masonry” layout’lar) listeleri doğrudan desteklemez.
* Öğe Konumu Dışı Etkileşimler: Öğelerin konumunu veya boyutunu etkileyecek dış değişiklikler (örneğin, CSS ile bir öğenin margin veya padding değerinin değişmesi), react-window‘un dahili hesaplamalarını bozabilir. Bu tür durumlarda resetAfterIndex gibi yöntemlerle manuel olarak kütüphaneyi bilgilendirmek gerekebilir.
* Karmaşık Başlık/Altlık Yapıları: Liste içinde sabit başlıklar veya altlıklar gibi karmaşık yapıları doğrudan desteklemez. Bu tür durumlar için ek DOM öğeleri veya özel bileşenler kullanmanız gerekebilir.
Alternatifler:
* react-virtualized: react-window‘un daha kapsamlı ve özellik zengini öncülüdür. Daha fazla bileşen (örneğin, Table, Collection, Masonry) ve daha gelişmiş özellikler sunar. Ancak, boyutu daha büyüktür ve API’si react-window‘a göre biraz daha karmaşıktır. Eğer react-window‘un sınırlamalarına takılıyorsanız ve daha fazla esnekliğe ihtiyacınız varsa, react-virtualized iyi bir seçenek olabilir.
* Özel Çözümler: Çok özel ve niş sanalizasyon ihtiyaçlarınız varsa, kendi sanalizasyon mantığınızı sıfırdan yazmanız gerekebilir. Ancak bu, önemli bir geliştirme çabası ve bakım maliyeti gerektirir.
* Tarayıcı Yerel Sanalizasyon (Gelecek): Web standartları geliştiricileri, tarayıcılara yerel sanalizasyon yetenekleri getirmek için çalışmaktadır. Bu, gelecekte kütüphanelere olan ihtiyacı azaltabilir.
Sonuç ve En İyi Uygulamalar
react-window, büyük listeleri verimli bir şekilde render etmek için React uygulamalarında olmazsa olmaz bir araçtır. Hafif yapısı, basit API’si ve üstün performansıyla, kullanıcı deneyimini önemli ölçüde iyileştirebilir.
Ne Zaman react-window Kullanmalıyız?
* Uygulamanızda binlerce veya on binlerce öğeden oluşan listeler veya ızgaralar varsa.
* Bu listelerin kaydırma performansından şikayet ediyorsanız.
* Öğe boyutları sabit veya önceden hesaplanabilirse (FixedSizeList).
* Öğe boyutları değişken ancak her öğe için kolayca hesaplanabilirse (VariableSizeList).
Performans İpuçları ve En İyi Uygulamalar:
1. itemData Kullanın: Verilerinizi itemData prop’u aracılığıyla geçirin. Bu, render prop’unuzun gereksiz kapanış (closure) sorunlarından kaçınmasını ve react-window‘un iç optimizasyonlarını daha etkili kullanmasını sağlar.
2. React.memo ile Öğeleri Optimize Edin: Liste öğeleriniz karmaşık bileşenlerse, React.memo ile sarmalayarak prop’ları değişmediği sürece yeniden render edilmelerini engelleyin.
3. Doğru itemSize Değerini Belirleyin: FixedSizeList için doğru itemSize değerini, VariableSizeList için ise mümkün olduğunca verimli çalışan bir getItemSize fonksiyonunu kullanın. getItemSize içinde pahalı hesaplamalardan kaçının ve boyutları önbelleğe almayı düşünün.
4. resetAfterIndex‘i Akıllıca Kullanın: VariableSizeList‘te bir öğenin boyutu değiştiğinde, sadece etkilenen bölümü yeniden hesaplamak için resetAfterIndex metodunu kullanın.
5. Gereksiz Render’lardan Kaçının: Listenizi çevreleyen bileşenin sık sık yeniden render edilmediğinden emin olun. React.memo veya useCallback/useMemo hook’larını kullanarak performansı artırabilirsiniz.
6. Erişilebilirlik (Accessibility): Sanal listeler erişilebilirlik açısından bazı zorluklar çıkarabilir. Klavye navigasyonu ve ekran okuyucuların doğru çalışmasını sağlamak için tabIndex gibi HTML özelliklerini ve ARIA rollerini doğru bir şekilde kullanmaya özen gösterin.
react-window‘u uygulamanıza entegre ederek, kullanıcılarınıza daha akıcı, daha hızlı ve daha keyifli bir deneyim sunabilir, aynı zamanda geliştirme sürecinizi basitleştirebilirsiniz. Büyük veri kümeleriyle çalışırken performans sorunlarıyla boğuşmak yerine, react-window ile bu sorunları zarifçe aşabilirsiniz.