React Query, React uygulamalarında veri getirme işlemlerini, gelen verinin önbelleğe alınmasını, senkronize edilmesini ve eskiyen verinin güncellenmesini kolaylaştıran bir kütüphanedir.
Vermiş olduğum örneklerin altına codesandbox örneği ekledim. Dolayısıyla yazıdaki kod örneklerinde kütüphane importlarını kalabalık olmaması için haricen eklemedim.
Öne çıkan özellikleri:
- Önbellekleme (sunucudan defalarca aynı veriyi çağırmanın önüne geçme)
- Verinin belli periyotlarla güncellenmesi, bu sürenin ayarlanması ve değişikliklerin hızlıca yansıtılması
- Verinin sayfa sayfa çağrılması veya lazy loading gibi işlemlerde performans optimizasyonu
- Server durumundaki bellek yönetimi ve garbage collection işlemlerinin yönetilmesi.
Bu noktadaki şahsi yorumlarından biri de React Query kütüphanesi kodun okunabilirliğini artırmakta ve asenkron verilerle çalışma stratejisini standartlaştırmaktadır.
Standart yöntem ile API çağrısı ve React query ile api çağrısı örneğini inceleyelim ve adım adım ekleme yapalım.
Klasik API Çağrı Örneği
// Standart çağrı örneği
function Classic() {
const [data, setData] = useState({});
const [error, setError] = useState();
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(
"api.github.com/repos/samilkahraman/m220"
);
const json = await response.json();
setIsLoading(false);
setData(json);
} catch (err) {
setError(err);
}
};
fetchData();
}, []);
if (isLoading) return "Yükleniyor...";
if (error) return "Hata meydana geldi: " + error.message;
return (
<div>
<h1>{data.name}</h1>
<p>{data.full_name}</p>
<strong>👀 {data.visibility}</strong>{" "}
</div>
);
}
Standart bir asenkron API çağrı örneği yaptık. Burada görüldüğü üzere hataları kendimiz yakalamalıyız ve yüklenme bilgisi için haricen bir durum yönetmeliyiz.
Bu yöntemi bir çok çağrı ile çalışan bir sistemde düzgün ve sistematik yönetmek bir süre sonra çok uzun zaman alıp yıldırıcı bir hale gelebilir.
Şimdi ise React Query ile aynı örneğe bakalım.
React Query API Çağrı Örneği
function ReactQueryExample() {
const { isLoading, error, data } = useQuery("repoData", () =>
fetch("api.github.com/repos/samilkahraman/m220").then((res) =>
res.json()
)
);
if (isLoading) return "Yükleniyor...";
if (error) return "Hata meydana geldi: " + error.message;
return (
<div>
<h1>{data.name}</h1>
<p>{data.full_name}</p>
<strong>👀 {data.visibility}</strong>{" "}
</div>
);
}
Görüldüğü üzere React Query kütüphanesi asenkron çağrıları kendi içerisinde yapmakta ve kod kalabalığından kurtarırken aynı şekilde sunucu durumunu kendisi kontrol etmektedir.
Kod örneği: Codesandbox
Şimdi asıl özelliklere gelebiliriz.
React Query Önbellek İşlemleri
useQuery veya useInfiniteQuery ile yapılan sorgular otomatik olarak eski/bayat(stale) olarak önbelleklenir.
Eski/Bayat sorgular aşağıdaki koşullarda otomatik olarak tekrar çağrılır.
- Tarayıcı penceresine yeniden gelindiğinde.
- İnternet bağlantısı yeniden sağlandığında
- Opsiyonel olarak belli aralıklarla çağrılması sağlandıysa
Örneğin sorgunun bayatlama/eskime süresini aşağıdaki gibi ayarlayabiliriz.
const second = 1000;
const minute = 60 * second;
const twoMinutes = 2 * minute;
const { isLoading, error, data } = useQuery("ornekCagri", () =>
fetch("api.github.com/repos/samilkahraman/m220").then((res) =>
res.json()
), {staleTime:twoMinutes}
);
Eklemiş olduğumuz {staleTime:twoMinutes} parametresiyle artık her 2 dakikada bir otomatik olarak verimiz çağrılacaktır. Bu şekilde verinin güncelliğiyle ilgili kaygıya gerek kalmayacaktır.
Bu parametre konulmadan önceki ve sonraki verinin "ReactQueryDevtools" ile görünümüne bakalım.
Öncelikli olarak staleTime belirtmeyince ne oluyor.
Şimdi ise {staleTime:twoMinutes} parametresini verdiğimizde ne olduğuna bakalım.
Görüldüğü üzere verinin güncelliğini staleTime parametresiyle kontrol edebiliyoruz. Bu kontrol işlemi standart yöntemlerle oldukça meşakkatli olacaktı.
Tekrar önbelleğe dönersek bu sorgu yapıldığı an itibariyle önbelleğe alınıyor ve uygulama içi farklı sayfalara geçtiğimizde React Query eski sorguları inactive olarak işaretliyor. Inactive sorgular default olarak 5 dakika sonra React Query Garbage Collector tarafından ön bellekten temizleniyor. Bu davranışı cacheTime parametresiyle birlikte uzatabilirsiniz.
Bu noktadan itibaren resmi dökümantasyon üzerindeki sıra ile hızlı bir özet yapmaya çalışacağım.
React Query Sorgu Yapıları
Kütüphaneyi kullanırken GET Talepleri için useQuery kullanılırken POST taleplerinde useMutation kullanılması önerilmektedir. Bu bağlamda yalnıza useQuery üzerinden anlatıma devam edeceğim fakat yazının ilerleyen bölümlerinde haricen useMutation kısmına değineceğim.
useQuery ile işlem yapılırken iki önemli parametre kullanılıyor:
const result = useQuery('uniqueKey', fetchData)
- Uniqe Key. Her bir sorgunuz eşsiz(unique) bir anahtar(key) ile yapılmalıdır. Eşsiz anahtarla birlikte aynı sorguyu kodunuzun farklı yerlerinden yönetme imkanı bulmaktasınız.
- Promise dönen fonksiyon Fonksiyon promise resolve etmeli ya da error throw etmelidir.
useQuery sonucunda result
objesi aşağıdaki durumları içermektedir.
- isLoading (Sorguda data yok fakat aktif bağlantıda yükleniyor)
- isError (Sorgu hata ile karşılaştı)
- isSuccess (Sorgu başarılı ve dataya erişilebilir)
Sorgu Anahtarları
Sorgularda eşsiz anahtar kullanılması gerektiğinden bahsettik fakat bu yöntemi uygulamamız belirli durumlarda zor olacaktır. Örneğin aşağıdaki tabloda görüldüğü gibi api/chat/:id
şeklinde bir uç noktadan bütün aktif mesajları tek bir tabloya çağırdığımızı düşünelim.
Burada data grid hücreleri içerisinde her bir projeye ait son chat mesajını getiriyoruz, fakat bunların her birini isimlendirmek için aynı key'i kullanamayız çünkü anahtarlar eşsiz olmalıdır. Burada imdadımıza React Query array ile isimlendirme yetişiyor.
useQuery(['fetchChatMessages', id], ...)
Yani sorgulardaki isimleri doğrudan string değil de array ile atayarak sorgulama yapıyoruz.
Görüldüğü gibi projeler tek bir sorgu ile gelirken, projelerin mesajları ayrı sorgularla ulaşılabilir konumda olduğundan aynı eşsiz anahtara ID eklenerek sorgulama yapılıyor.
React Query Paralel Sorgular
Bir sayfada birden fazla kaynağın yüklendiği bir çok senaryo mevcut. Bu durumlarda React Query'yi paralel olarak kullanmanızda bir problem olmamakta.
const usersQuery = useQuery('users', fetchUsers)
const teamsQuery = useQuery('teams', fetchTeams)
const projectsQuery = useQuery('projects', fetchProjects)
Fakat burada dikkat edilmesi gereken yazının başındaki örnekteki gibi isLoading, data, error durumlarını doğrudan çıkartamadık.
const { isLoading, error, data } = useQuery('projects', fetchProjects)
Fakat halen objenin parametrelerine ulaşabildiğimiz için farklı yöntemlerle benzer şekilde kullanabiliriz.
const fetchUsers = () =>
fetch("api.github.com/repos/samilkahraman/m220").then((res) =>
res.json()
);
// Hata dönmesi için özellikle github yerine gihub yazıldı
const fetchTeams = () =>
fetch("api.gihub.com/repos/samilkahraman/m220").then((res) =>
res.json()
);
const fetchProjects = () =>
fetch("api.github.com/repos/samilkahraman/m220").then((res) =>
res.json()
);
function ReactParallelQueryExample() {
const usersQuery = useQuery("users", fetchUsers);
const teamsQuery = useQuery("teams", fetchTeams);
const projectsQuery = useQuery("projects", fetchProjects);
const { isLoading, error, data } = useQuery("ornekCagri", () =>
fetch("api.github.com/repos/samilkahraman/m220").then((res) =>
res.json()
)
);
if (isLoading) return "Yükleniyor...";
if (error) return "Hata meydana geldi: " + error.message;
return (
<>
<div>
Kullanıcı Sorgusu Durumu: {conditionalColoring(usersQuery.status)}
</div>
<div>
Takımlar Sorgusu Durumu: {conditionalColoring(teamsQuery.status)}
</div>
<div>
Projeler Sorgusu Durumu: {conditionalColoring(projectsQuery.status)}
</div>
<p>{data.full_name}</p>
</>
);
}
const conditionalColoring = (status) =>
status === "success" ? <>🟢 success</> : <>🔴 error</>;
React Query Bağımlı Sorgular
Eğer sorgunun yapılabilmesi kendisinden önceki sorgulara bağlı ise sadece bir parametre ile bunu sağlayabilirsiniz. { enabled:false }
// Kullanıcıyı getir
const { data: user } = useQuery(['user', email], getUserByEmail)
const userId = user?.id
// Kullanıcının projesini getir
const { isIdle, data: projects } = useQuery(
['projects', userId],
getProjectsByUser,
{
// Kullanıcı ID'si gelene kadar sorgu çalışmayacaktır.
enabled: !!userId,
}
)
Birinci bölüm sonu
Buraya kadar olan kısım React Query giriş seviyesi için yeterli olduğunu düşündüğümden yazının daha fazla ilerlememesi için sonlandıracağım. Bir sonraki bölümde bir adım ilerisinde neler yapılabileceğini aynı şekilde anlatmaya çalışacağım. Umarım merak edenlere faydalı olur.