React uygulamalarınızda useEffect kullanarak veri çekme pratiklerinin neden sorunlu olabileceğini ve daha modern, etkili alternatifleri bu kapsamlı rehberde keşfedin. Performans sorunları, hata yönetimi karmaşıklığı ve kullanıcı deneyimi gibi kritik konulara çözüm bulacağız.
React ekosistemi, kullanıcı arayüzleri oluşturmak için geliştiricilere güçlü araçlar sunar. Bu araçlardan biri olan useEffect, bileşen yaşam döngüsü yan etkilerini (side effects) yönetmek için tasarlanmıştır. Veri çekme, abonelikler, manuel DOM manipülasyonları gibi işlemler tipik yan etkilerdir. Ancak, useEffect‘in veri çekme için yaygın olarak kullanılması, zamanla çeşitli zorlukları ve antipattern’ları beraberinde getirmiştir. Özellikle tek sayfalık uygulamaların (SPA) karmaşıklığı arttıkça, useEffect ile veri çekme, beklenenden daha fazla baş ağrısına yol açabilmektedir.
Geliştiriciler, bir bileşen yüklendiğinde veya belirli bağımlılıklar değiştiğinde bir API çağrısı yapma ihtiyacı duyduğunda doğal olarak useEffect‘e yönelirler. Çünkü bu, “bir şeyler değiştiğinde bir şeyler yap” mantığına en uygun React kancası gibi görünür. Ancak bu yaklaşım, özellikle büyük ölçekli uygulamalarda veya sık veri güncellemesi gerektiren senaryolarda, performans düşüşlerine, gereksiz yeniden render’lara, sonsuz döngülere ve daha da önemlisi, yarış koşulları (race conditions) gibi zor tespit edilebilir hatalara neden olabilir. Örneğin, kullanıcı hızlıca sayfalar arasında gezinirken, önceki sayfanın veri çekme isteği henüz tamamlanmamışken yeni bir sayfanın isteği tetiklenebilir. Eğer önceki isteğin sonucu, yeni sayfanın durumu güncelleyecek şekilde işlenirse, bu durum uygulamanızda beklenmedik ve hatalı bir state’e yol açabilir. Bu senaryolar, useEffect‘in doğasında var olan asenkronluk ve bağımlılık yönetimi karmaşıklığından kaynaklanır.
Bu sorunlar sadece teknik karmaşıklıkla sınırlı kalmaz; aynı zamanda geliştirici deneyimini de olumsuz etkiler. Hata ayıklama süreçleri uzar, kodun okunabilirliği ve bakımı zorlaşır. Bir useEffect bloğunun ne zaman çalışacağını, hangi bağımlılıkların tetiklediğini ve olası yan etkilerini anlamak, özellikle birden fazla useEffect kancasının iç içe kullanıldığı durumlarda karmaşık bir bulmacaya dönüşebilir. Bu durum, yeni ekip üyelerinin projeye adaptasyonunu yavaşlatabilir ve mevcut geliştiricilerin verimliliğini düşürebilir. Veri çekme, çoğu React uygulamasının temel bir parçası olduğundan, bu sürecin optimize edilmiş ve hata toleranslı olması kritik önem taşır. Bu makalede, useEffect‘in bu tür sınırlılıklarını aşmanın ve daha sağlam, performanslı ve bakımı kolay veri çekme stratejileri geliştirmenin yollarını keşfedeceğiz. Artık useEffect‘in sadece yan etkiler için ayrılmış, daha özel bir araç olarak kullanılması gerektiğini savunuyoruz.
useEffect’in Amacı Neydi ve Veri Çekmede Nasıl Yanlış Anlaşıldı?
useEffect kancası, React ekibi tarafından bileşenlerin yaşam döngüsü yan etkilerini (side effects) yönetmek için tasarlanmıştır. Bir bileşenin render edilmesinin ötesine geçen her türlü işlem, bir yan etki olarak kabul edilebilir. Örneğin, bir API’den veri getirme, bir bileşen yüklendiğinde bir timer başlatma, DOM’u doğrudan manipüle etme veya bir olay dinleyicisi ekleme gibi işlemler useEffect‘in temel kullanım alanlarıdır. Temel felsefesi, iş mantığını ve sunum mantığını ayrı tutarken, bileşenin dış dünyayla etkileşimini yönetmektir. Bağımlılık dizisi (dependency array) ile useEffect, sadece belirli değerler değiştiğinde yan etkinin yeniden çalıştırılmasını sağlayarak performans optimizasyonu ve kontrol mekanizması sunar. Bu kanca, sınıf tabanlı bileşenlerdeki componentDidMount, componentDidUpdate ve componentWillUnmount yaşam döngüsü metodlarının işlevselliğini tek bir API altında birleştirmeyi amaçlamıştır.
Ancak, veri çekme, özellikle React’a yeni başlayan geliştiriciler için useEffect içinde uygulanması “doğal” veya “kolay” görünen bir senaryo olmuştur. Bir bileşen yüklendiğinde veri getirmek istendiğinde, boş bir bağımlılık dizisi ([]) ile useEffect kullanmak, componentDidMount‘a eşdeğer bir davranış sunar gibi görünür. Ancak bu yaklaşım, asenkron işlemlerin doğası gereği ortaya çıkan karmaşıklıkları göz ardı eder. Basit bir “veri getir ve göster” senaryosu bile, yükleme durumu, hata durumu, önbellekleme, tekrar deneme (retry) mekanizmaları ve verinin güncelliği gibi birçok ek yönetimi gerektirir. useEffect‘in temel amacı bu karmaşık veri yönetimi stratejilerini baştan sona uygulamak değil, belirli bir koşul altında bir yan etkiyi tetiklemek ve temizlemektir (cleanup).
Veri çekme, çoğu zaman uygulamanın global bir durumunun veya dış dünyanın bir parçasıdır. useEffect içine yerleştirilen veri çekme mantığı, bileşenin kendi içinde bir mikro-yönetim sistemi oluşturur. Bu da kod tekrarına, senkronizasyon sorunlarına ve uygulamanın genel veri akışını anlamayı zorlaştırmaya yol açar. Örneğin, aynı veriye ihtiyaç duyan iki farklı bileşen varsa, her ikisi de useEffect içinde kendi veri çekme mantığını yazmaya başlayabilir. Bu durum, gereksiz ağ isteklerine ve önbellekleme fırsatlarının kaçırılmasına yol açar. Dahası, useEffect içindeki asenkron işlemlerin yönetimi (örneğin, bir bileşen mount olmaktan çıktığında bekleyen bir isteği iptal etme), manuel olarak yapılmazsa hafıza sızıntılarına veya “state güncellenirken bileşen mount olmadı” uyarılarına neden olabilir. Bu yanlış anlama ve kullanım, useEffect‘in potansiyelini azaltırken, uygulamaların performansını ve bakımını olumsuz etkileyen pratiklerin yaygınlaşmasına neden olmuştur.
Daha Temiz ve Verimli Veri Çekme: Custom Hook’lar Nasıl Yardımcı Olur?
useEffect‘in veri çekme için sınırlılıklarını anladıktan sonra, daha temiz ve yeniden kullanılabilir bir yaklaşım arayışına gireriz. Bu noktada, React’ın sağladığı güçlü bir özellik olan “Custom Hook”lar devreye girer. Custom Hook’lar, stateful mantığı bileşenler arasında yeniden kullanmamızı sağlayan fonksiyonlardır. useEffect, useState gibi diğer React kancalarını içinde barındırarak, veri çekme mantığını soyutlamanın ve bileşenlerimizden ayırmanın mükemmel bir yolunu sunarlar. Bu, kod tekrarını azaltır, okunabilirliği artırır ve bakımını kolaylaştırır.
Bir Custom Hook kullanarak veri çekme mantığını nasıl soyutlayabiliriz? Temel fikir, bir API uç noktası alır, bu uç noktadan veri çeker ve uygulamanın yükleme, hata ve veri durumlarını döndüren bir fonksiyon oluşturmaktır. Böylece, her bileşenin kendi içinde useState ve useEffect kancalarını tekrarlaması yerine, bu mantığı tek bir yerden yönetebiliriz. Aşağıda basit bir useFetch Custom Hook örneği bulunmaktadır:
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(HTTP error! status: ${response.status});
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]); // url değiştiğinde veriyi yeniden çek
return { data, loading, error };
}
// Custom Hook'un kullanımı
function UserProfile({ userId }) {
const { data: user, loading, error } = useFetch(/api/users/${userId});
if (loading) return Kullanıcı bilgileri yükleniyor...
;
if (error) return Hata: {error.message}
;
return (
{user.name}
Email: {user.email}
);
}
Bu useFetch Custom Hook'u, veri çekme işleminin tüm döngüsünü kapsar: isteği başlatmadan önce yükleme durumunu ayarlar, başarılı bir şekilde veriyi aldığında durumu günceller, bir hata oluştuğunda hatayı yakalar ve son olarak yükleme durumunu sonlandırır. Bu hook sayesinde, UserProfile gibi bileşenler sadece alınan veriyi göstermeye odaklanır, veri çekme mantığının detaylarıyla ilgilenmezler. Bu ayrım, bileşenlerinizi daha saf (pure) ve test edilebilir hale getirir.
Custom Hook'lar, sadece veri çekme mantığını kapsamakla kalmaz, aynı zamanda önbellekleme (caching), verinin arka planda yeniden doğrulanması (revalidation) veya hata anında otomatik tekrar deneme gibi daha gelişmiş özellikler eklemek için de genişletilebilir. Örneğin, bir memo mekanizması ekleyerek aynı URL için gereksiz yere birden fazla istek atılmasını engelleyebilirsiniz. Bu, uygulamanızın performansını doğrudan etkileyen önemli bir iyileştirmedir. Custom Hook'lar, useEffect'in kendisini kötü bir araç olmaktan çıkarıp, yan etkileri yönetmek için doğru bağlamda kullanıldığında ne kadar güçlü olabileceğini gösteren bir örnektir. Veri çekme gibi karmaşık ve tekrarlayan görevleri soyutlayarak, React uygulamalarınızda daha sürdürülebilir bir mimari oluşturmanıza yardımcı olurlar. Bu yaklaşım, sadece temiz kod yazmakla kalmaz, aynı zamanda uygulamalarınızın ölçeklenebilirliğini de artırır.
Uzman İpucu: Custom Hook'larınızı mümkün olduğunca jenerik tutmaya çalışın. Bu, sadece belirli bir API endpoint'i için değil, farklı veri çekme senaryolarında da kullanılabilir olmalarını sağlar. Örneğin, bir kimlik doğrulama başlığı eklemek için bir parametre kabul edebilirler.
React Query (TanStack Query) ve SWR: Veri Çekme Kütüphanelerinin Gücü
Custom Hook'lar, veri çekme mantığını soyutlamak için harika bir ilk adımdır, ancak modern web uygulamalarının veri çekme ihtiyaçları, basit bir useFetch hook'unun ötesine geçer. Önbellekleme, verinin güncelliği (stale-while-revalidate), otomatik tekrar denemeler, iyimser güncellemeler ve eşzamanlı istek yönetimi gibi konular, elle implemente edildiğinde ciddi bir karmaşıklığa yol açar. İşte tam bu noktada, React Query (şimdiki adıyla TanStack Query) ve SWR (Stale-While-Revalidate) gibi veri çekme kütüphaneleri devreye girer. Bu kütüphaneler, birer "veri katmanı" veya "veri senkronizasyon araçları" olarak işlev görerek, sunucu verisiyle etkileşim kurmanın ve durumu yönetmenin standart bir yolunu sunar.
React Query (TanStack Query) ile Akıllı Veri Yönetimi
React Query, "sunucu state'ini" yönetmek için kapsamlı bir çözüm sunar. Uygulamanızın UI state'inden (useState ile yönetilen) ayrı bir şekilde sunucudan gelen veriyi ele alır. Bu, birçok yaygın veri çekme problemini (önbellekleme, senkronizasyon, güncelleme) sizin yerinize çözdüğü anlamına gelir. React Query'nin temel özellikleri şunlardır:
- Önbellekleme (Caching): Otomatik olarak veriyi önbelleğe alır ve gereksiz ağ isteklerini önler. Aynı veriye ihtiyaç duyan birden fazla bileşen varsa, React Query yalnızca bir kez istek yapar ve sonucu tüm bileşenlere servis eder.
- Arka Plan Revalidasyonu: Veri "stale" (eskimiş) kabul edildiğinde, kullanıcıya anında önbelleğe alınmış veriyi gösterirken arka planda yeni veriyi çekmeye başlar. Yeni veri geldiğinde UI güncellenir. Bu, mükemmel bir kullanıcı deneyimi sağlar.
- Otomatik Tekrar Deneme (Retries): Ağ istekleri başarısız olduğunda otomatik olarak belirli bir sayıda tekrar deneme yapar.
- İyimser Güncellemeler (Optimistic Updates): Bir API isteği göndermeden önce UI'yı güncelleyerek anında geri bildirim sağlar. İstek başarısız olursa UI eski haline döner.
- Verimli Hata Yönetimi: Global hata yöneticileri ve bileşen bazlı hata durumları için kolay entegrasyon.
- Sonsuz Kaydırma (Infinite Scrolling) ve Sayfalama: Karmaşık veri listelerini yönetmek için özel hook'lar sunar.
React Query'nin kullanımı oldukça basittir. Temel olarak, useQuery kancasını kullanarak verinizi çekersiniz:
import { useQuery, QueryClient, QueryClientProvider } from '@tanstack/react-query';
// QueryClient oluşturulur ve uygulamanızın tepesine yerleştirilir
const queryClient = new QueryClient();
function Todos() {
const { data, isLoading, error } = useQuery({
queryKey: ['todos'], // Bu sorguyu tanımlayan eşsiz bir anahtar
queryFn: async () => { // Veri çekme fonksiyonu
const response = await fetch('/api/todos');
if (!response.ok) {
throw new Error('Yapılacaklar listesi yüklenemedi.');
}
return response.json();
}
});
if (isLoading) return Yapılacaklar yükleniyor...
;
if (error) return Bir hata oluştu: {error.message}
;
return (
{data.map(todo => (
- {todo.title}
))}
);
}
function App() {
return (
);
}
Bu örnekte, useQuery kancası ['todos'] anahtarıyla tanımlanmış bir sorgu çalıştırır. queryFn içinde veri çekme mantığımız bulunur. React Query, bu veriyi otomatik olarak önbelleğe alır, yükleme ve hata durumlarını yönetir. Siz sadece veriyi kullanmaya odaklanırsınız.
SWR: Hafif ve Etkili Bir Alternatif
SWR, Next.js ekibi tarafından geliştirilmiş, "Stale-While-Revalidate" (eskimişken yeniden doğrula) stratejisini temel alan hafif bir veri çekme kütüphanesidir. Adından da anlaşılacağı gibi, SWR önbelleğe alınmış (stale) veriyi anında gösterirken, arka planda yeni veriyi (revalidate) çekerek güncelliğini kontrol eder. Bu, kullanıcıya neredeyse anında geri bildirim sağlarken, her zaman en güncel veriyi görmesini garantiler. SWR'ın başlıca avantajları:
- Basit API: Öğrenmesi ve kullanması oldukça kolaydır.
- Otomatik Önbellekleme ve Revalidasyon: Veriyi otomatik olarak önbelleğe alır ve belirli olaylarda (pencereye odaklanma, ağ bağlantısının yeniden kurulması) yeniden doğrulama yapar.
- Tekrar Denemeler: İstekler başarısız olduğunda tekrar deneme mekanizması sunar.
- Fokus Takibi: Tarayıcı penceresi yeniden odaklandığında otomatik olarak veriyi yeniler, bu da uygulamanızın her zaman güncel kalmasını sağlar.
import useSWR from 'swr';
const fetcher = (...args) => fetch(...args).then(res => res.json());
function UserProfile({ userId }) {
const { data, error, isLoading } = useSWR(/api/users/${userId}, fetcher);
if (isLoading) return Profil yükleniyor...;
if (error) return Kullanıcı bilgileri yüklenirken bir hata oluştu: {error.message};
if (!data) return Kullanıcı bulunamadı.;
return (
{data.name}
Email: {data.email}
);
}
Yukarıdaki örnekte, useSWR kancası bir anahtar (genellikle API URL'si) ve bir fetcher fonksiyonu alır. SWR, bu fonksiyonu kullanarak veriyi çeker ve tüm önbellekleme, yükleme, hata ve revalidasyon mantığını sizin için yönetir. Her iki kütüphane de useEffect'in veri çekme için manuel olarak yönetilmesi gereken birçok karmaşıklığını ortadan kaldırarak geliştirici verimliliğini ve kullanıcı deneyimini önemli ölçüde artırır. Hangi kütüphaneyi seçeceğiniz, projenizin ihtiyaçlarına ve ekibinizin tercihlerine bağlıdır; ancak her ikisi de useEffect ile veri çekme pratiklerine göre çok daha üstün çözümler sunar.
Uzman İpucu: Büyük ölçekli uygulamalarda, global state yönetimi için Redux veya Zustand gibi kütüphanelerle React Query/SWR'ı birleştirebilirsiniz. React Query/SWR sunucu state'i için mükemmelken, Redux/Zustand istemci tarafı UI state'i için daha uygundur.
Vaka Analizi: Ürün Listeleme Sayfasında useEffect vs. React Query
Bir e-ticaret uygulamasında sıkça karşılaşılan senaryolardan biri, bir ürün listeleme sayfasıdır. Bu sayfa genellikle ürünleri kategoriye, fiyata veya popülerliğe göre filtreleme ve sıralama seçenekleri sunar. Gelin, bu senaryoyu useEffect ile ve React Query ile nasıl ele alacağımızı karşılaştıralım ve aradaki farkları gözlemleyelim.
Senaryo 1: useEffect ile Ürün Listeleme (Geleneksel Yaklaşım)
useEffect kullanarak bir ürün listesi çekerken, genellikle bir veya daha fazla useState kancasıyla yükleme durumu, hata durumu ve ürün verisi yönetilir. Filtreleme veya sıralama parametreleri değiştiğinde, useEffect'in bağımlılık dizisindeki bu parametreler değiştiği için veri tekrar çekilir.
import React, { useState, useEffect } from 'react';
function ProductListWithUseEffect() {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [category, setCategory] = useState('all'); // Varsayılan kategori
const [sortBy, setSortBy] = useState('price'); // Varsayılan sıralama
useEffect(() => {
const fetchProducts = async () => {
setLoading(true);
setError(null);
try {
// API URL'ine kategori ve sıralama parametreleri eklenir
const response = await fetch(/api/products?category=${category}&sortBy=${sortBy});
if (!response.ok) {
throw new Error(HTTP error! status: ${response.status});
}
const data = await response.json();
setProducts(data);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchProducts();
}, [category, sortBy]); // Kategori veya sıralama değiştiğinde tekrar çek
if (loading) return Ürünler yükleniyor...
;
if (error) return Ürünler yüklenirken bir hata oluştu: {error.message}
;
return (
Ürünlerimiz
{products.map(product => (
- {product.name} - {product.price} TL
))}
);
}
Bu yaklaşımın sorunları:
- Önbellekleme Yok: Kullanıcı aynı kategoriye geri döndüğünde veya filtreleri değiştirdiğinde, her seferinde yeni bir ağ isteği yapılır.
- Kullanıcı Deneyimi: Her filtre değişikliğinde "Yükleniyor..." mesajı belirir, bu da akıcı olmayan bir kullanıcı deneyimi yaratır.
- Manuel Yönetim: Yükleme, hata ve veri durumlarının her bileşende manuel olarak yönetilmesi gerekir, bu da kod tekrarına yol açar.
- Yarış Koşulları: Kullanıcı filtreleri çok hızlı değiştirirse, eski bir isteğin sonucu daha yeni bir isteğin sonucunu geçersiz kılabilir.
Senaryo 2: React Query ile Ürün Listeleme (Modern Yaklaşım)
React Query kullanarak, veri çekme mantığı sadeleşir ve birçok karmaşık durum kütüphane tarafından otomatik olarak yönetilir.
import React, { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
function ProductListWithReactQuery() {
const [category, setCategory] = useState('all');
const [sortBy, setSortBy] = useState('price');
const { data: products, isLoading, error } = useQuery({
queryKey: ['products', category, sortBy], // Anahtar olarak filtre ve sıralama parametreleri kullanılır
queryFn: async () => {
const response = await fetch(/api/products?category=${category}&sortBy=${sortBy});
if (!response.ok) {
throw new Error(HTTP error! status: ${response.status});
}
return response.json();
},
staleTime: 5 * 60 * 1000, // Veri 5 dakika boyunca 'fresh' kabul edilsin
cacheTime: 10 * 60 * 1000, // Veri 10 dakika boyunca önbellekte kalsın
});
if (isLoading) return Ürünler yükleniyor...
;
if (error) return Ürünler yüklenirken bir hata oluştu: {error.message}
;
return (
Ürünlerimiz
{products.map(product => (
- {product.name} - {product.price} TL
))}
);
}
React Query'nin avantajları:
- Otomatik Önbellekleme:
queryKeydizisindeki parametreler değiştiğinde React Query yeni bir sorgu anahtarı oluşturur. Daha önce aynı anahtarla çekilmiş veri varsa, tekrar istek atmadan önbellekten getirir. Bu, anlık kullanıcı deneyimi sağlar. - Arka Plan Yeniden Doğrulama: Kullanıcı filtre değiştirdiğinde anında eski veriyi gösterir ve arka planda yeni veriyi çeker. Yeni veri geldiğinde UI sorunsuz bir şekilde güncellenir. "Yükleniyor..." spinner'ını daha az görürsünüz.
- Basit Hata ve Yükleme Yönetimi:
isLoadingveerrordeğişkenleri doğrudanuseQuery'den gelir, manueluseStatetanımlamalarına gerek kalmaz. - Yarış Koşulları Yok: React Query, birden fazla isteğin aynı anda tetiklenmesi durumunda, yalnızca en son isteğin sonucunu UI'ya yansıtır, böylece yarış koşulları engellenmiş olur.
Bu vaka analizi, React Query gibi kütüphanelerin sadece kod yazmayı kolaylaştırmakla kalmayıp, aynı zamanda uygulamanın performansını, güvenilirliğini ve genel kullanıcı deneyimini nasıl temelden iyileştirdiğini açıkça göstermektedir. useEffect hala yan etkiler için değerli bir araçtır, ancak veri çekme gibi sunucu state'i yönetimi gerektiren karmaşık görevler için özel olarak tasarlanmış çözümler kullanmak çok daha verimli ve sürdürülebilir bir yaklaşımdır.
İleri Düzey Veri Çekme Optimizasyonları ve Performans İpuçları
Modern React uygulamalarında veri çekme, uygulamanın performansını ve kullanıcı deneyimini doğrudan etkileyen kritik bir bileşendir. useEffect'ten vazgeçip React Query veya SWR gibi kütüphaneleri benimsedikten sonra, bu kütüphanelerin sunduğu ileri düzey optimizasyonlardan nasıl faydalanabileceğinize dair bazı ipuçları bulunmaktadır.
1. İyimser Güncellemeler (Optimistic Updates) ile Anında Geri Bildirim
Kullanıcı bir işlem yaptığında (örneğin bir öğeyi silme veya favorilere ekleme), API isteğinin tamamlanmasını beklemek yerine UI'yı anında güncellemek, kullanıcıya "anında" geri bildirim sağlar ve uygulamanızın daha duyarlı hissetmesine neden olur. API isteği başarılı olursa UI bu şekilde kalır, başarısız olursa UI eski haline döner.
React Query'de useMutation kancası bu senaryo için idealdir:
import { useMutation, useQueryClient } from '@tanstack/react-query';
function TodoItem({ todo }) {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (newTodo) => fetch(/api/todos/${todo.id}, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newTodo),
}).then(res => res.json()),
onMutate: async (newTodo) => {
// Önceki sorguyu iptal et
await queryClient.cancelQueries({ queryKey: ['todos'] });
// Önceki veriyi sakla
const previousTodos = queryClient.getQueryData(['todos']);
// UI'yı iyimser olarak güncelle
queryClient.setQueryData(['todos'], old =>
old.map(t => (t.id === todo.id ? { ...t, ...newTodo } : t))
);
return { previousTodos }; // Context olarak geri döndür
},
onError: (err, newTodo, context) => {
// Hata oluşursa UI'yı eski haline getir
queryClient.setQueryData(['todos'], context.previousTodos);
console.error('Görev güncellenemedi:', err);
},
onSettled: () => {
// Başarılı veya hatalı olsun, tüm 'todos' sorgularını yeniden doğrula
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
});
const handleToggleDone = () => {
mutation.mutate({ ...todo, completed: !todo.completed });
};
return (
{todo.title}
);
}
Bu örnekte, kullanıcı bir görevi tamamladığında, API isteği devam ederken UI anında güncellenir. Eğer istek başarısız olursa, onError fonksiyonu sayesinde UI eski haline döner. Bu, kullanıcının bekleme süresini algılamasını azaltır.
2. Önceden Çekme (Prefetching) ile Yükleme Sürelerini Kısaltma
Kullanıcının bir sonraki sayfaya veya bileşene gitme olasılığını önceden tahmin ederek ilgili verileri arka planda çekmek, yükleme sürelerini önemli ölçüde kısaltır. Örneğin, bir ürün listesinde kullanıcı bir ürünün üzerine geldiğinde, o ürünün detay sayfasının verilerini önceden çekebilirsiniz.
import { useQueryClient } from '@tanstack/react-query';
function ProductCard({ productId, productName }) {
const queryClient = useQueryClient();
const prefetchProductDetails = () => {
queryClient.prefetchQuery({
queryKey: ['product', productId],
queryFn: async () => {
const response = await fetch(/api/products/${productId});
if (!response.ok) throw new Error('Ürün detayları yüklenemedi');
return response.json();
},
staleTime: 5 * 60 * 1000, // Prefetch edilen veri de önbellekte tutulabilir
});
};
return (
{productName}
Ürün ID: {productId}
{/* Detay sayfasına giden bir link */}
/products/${productId}}>Detayları Görüntüle
);
}
Bu örnekte, kullanıcı fareyi bir ürün kartının üzerine getirdiğinde, o ürünün detayları arka planda çekilmeye başlar. Kullanıcı detay sayfasına tıkladığında, veri zaten önbellekte olacağı için sayfa neredeyse anında yüklenir.
3. Mobil Uyumlu Tasarım ve Medya Sorguları
Veri çekme performansı kadar, uygulamanızın farklı cihazlarda nasıl göründüğü ve çalıştığı da önemlidir. Mobil uyumlu bir tasarım, iyi bir kullanıcı deneyimi için vazgeçilmezdir. Medya sorguları (media queries) kullanarak, ekran boyutuna göre stil ve hatta veri çekme stratejilerini ayarlayabilirsiniz.
/* style.css veya styled-components içinde */
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
}
@media (max-width: 768px) {
.product-grid {
grid-template-columns: 1fr; /* Küçük ekranlarda tek sütun */
}
/* Mobil cihazlar için daha az veri çekme veya farklı layoutlar */
/* Örneğin, küçük resimler için düşük çözünürlüklü API'ler kullanmak */
}
Bu CSS medya sorgusu örneği, ürün listesini daha büyük ekranlarda bir grid düzeninde, mobil cihazlarda ise tek sütunlu bir düzende gösterir. Veri çekme açısından, mobil cihazlar için daha az sayıda öğe çekmek veya daha küçük boyutlu görseller kullanmak gibi stratejilerle ağ yükünü azaltabilirsiniz. Örneğin, useQuery kancanızda, ekran boyutuna göre farklı limit parametreleri gönderebilirsiniz.
// Örnek: Ekran boyutuna göre ürün limitini ayarlama
function useScreenSize() {
const [size, setSize] = useState('large'); // 'small', 'medium', 'large'
useEffect(() => {
const handleResize = () => {
if (window.innerWidth < 600) setSize('small');
else if (window.innerWidth < 1024) setSize('medium');
else setSize('large');
};
window.addEventListener('resize', handleResize);
handleResize(); // İlk yüklemede çalıştır
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
function ResponsiveProductList() {
const screenSize = useScreenSize();
const productLimit = screenSize === 'small' ? 10 : 20; // Mobil için daha az ürün
const { data: products } = useQuery({
queryKey: ['products', productLimit],
queryFn: async () => {
const response = await fetch(/api/products?limit=${productLimit});
return response.json();
},
});
// ... bileşen render mantığı
}
Bu ileri düzey teknikler, uygulamanızın sadece düzgün çalışmasını sağlamakla kalmaz, aynı zamanda kullanıcılara hızlı, duyarlı ve keyifli bir deneyim sunar. useEffect'ten vazgeçmek, bu tür optimizasyonların önünü açar ve sizi modern React geliştirme pratikleriyle tanıştırır.
Sonuç: useEffect'ten Veri Çekme Mirasını Geride Bırakmak
React uygulamalarında veri çekme, geliştirme sürecinin temel taşlarından biridir. Bu makale boyunca, useEffect kancasının yan etkileri yönetmek için mükemmel bir araç olmasına rağmen, veri çekme gibi sunucu state'i gerektiren görevlerde neden yetersiz kaldığını ve beraberinde getirdiği karmaşıklıkları detaylıca inceledik. Sonsuz döngüler, yarış koşulları, manuel önbellekleme ve hata yönetimi gibi sorunlar, geliştiricilerin zamanını boşa harcarken, kullanıcı deneyimini de olumsuz etkileyen önemli engeller oluşturuyordu.
Ancak, bu sorunların üstesinden gelmek için güçlü ve modern alternatiflerin mevcut olduğunu gördük. Custom Hook'lar, veri çekme mantığını bileşenlerimizden soyutlayarak daha temiz ve yeniden kullanılabilir kod yazmamıza olanak tanırken, React Query ve SWR gibi kütüphaneler veri yönetimini tamamen yeni bir seviyeye taşıyor. Bu kütüphaneler, otomatik önbellekleme, arka plan revalidasyonu, iyimser güncellemeler ve akıllı tekrar denemeler gibi gelişmiş özelliklerle geliştiricilere zaman kazandırırken, uygulamaların performansını ve duyarlılığını artırıyor. Vaka analizlerimiz, bu kütüphanelerin bir ürün listeleme sayfasında dahi nasıl devrim yarattığını somut örneklerle gösterdi.
useEffect'i veri çekme için kullanmaktan vazgeçmek, sadece daha az hata ayıklamak veya daha az kod yazmak anlamına gelmez; aynı zamanda uygulamanızın genel mimarisini iyileştirmek, geliştirici verimliliğini artırmak ve kullanıcılarınıza daha akıcı, daha hızlı ve daha güvenilir bir deneyim sunmak demektir. useEffect'i gerçek amacı olan yan etkileri temiz bir şekilde yönetmek için saklarken, veri çekme gibi karmaşık senaryolar için özel olarak tasarlanmış çözümlere yönelmek, modern React geliştirmenin olmazsa olmazıdır. Bu geçiş, şüphesiz ki hem sizin hem de kullanıcılarınız için çok daha verimli ve keyifli bir yazılım deneyimi anlamına gelecektir.
Sıkça Sorulan Sorular
- React'ta veri çekmek için
useEffectkullanmayı tamamen bırakmalı mıyım? - Hayır, tamamen bırakmanız gerekmez.
useEffect'i veri çekmek için kullanmak, genellikle daha iyi alternatifler olduğu için önerilmez. Ancak, çok basit ve tek seferlik, önbellekleme, yeniden deneme veya hata yönetimi gerektirmeyen durumlarda teknik olarak hala çalışır. Yine de, Custom Hook'lar veya React Query/SWR kullanmak çoğu senaryoda daha iyi bir pratik olacaktır. - Custom Hook'lar ve React Query/SWR arasındaki fark nedir?
- Custom Hook'lar,
useStateveuseEffectgibi temel React kancalarını kullanarak kendi veri çekme mantığınızı soyutlamanıza olanak tanır. React Query ve SWR ise, önbellekleme, revalidasyon, hata yönetimi ve iyimser güncellemeler gibi birçok gelişmiş özelliği hazır olarak sunan kapsamlı veri çekme kütüphaneleridir. Genellikle, basit durumlarda Custom Hook'lar yeterli olabilirken, daha karmaşık ve performans odaklı uygulamalar için React Query/SWR tercih edilir. - Hangi veri çekme kütüphanesini seçmeliyim: React Query mi SWR mı?
- Her iki kütüphane de mükemmel seçeneklerdir ve benzer özellikler sunar. React Query (TanStack Query) genellikle daha kapsamlı özellik setine sahiptir (örneğin, mutasyonlar, sonsuz kaydırma) ve daha büyük, karmaşık uygulamalar için tercih edilebilir. SWR ise daha hafif, minimalist bir API sunar ve özellikle Next.js ile birlikte popülerdir. Seçiminiz projenizin özel ihtiyaçlarına, ekibinizin alışkanlıklarına ve tercihlerinize bağlıdır.
- Veri çekme kütüphaneleri, state yönetim kütüphanelerinin (Redux, Zustand) yerini alabilir mi?
- Kısmen evet, ancak tamamen değil. React Query/SWR gibi kütüphaneler "sunucu state'ini" (yani API'lerden gelen veriyi) yönetmek için tasarlanmıştır. Bu verinin önbelleklenmesi, güncellenmesi ve senkronizasyonu konusunda harikadırlar. Ancak, uygulamanızın "istemci state'ini" (örneğin, bir formun input değerleri, açık/kapalı bir modal durumu gibi UI state'i) yönetmek için Redux, Zustand veya React'ın kendi
useState/useReducerkancaları hala gereklidir. Bu iki tür state yönetimi genellikle birlikte kullanılır.