ViraStack
  • Akademi
  • Hakkında
  • Destek
Genel BakışKaynaklarKatkı
ViraStack

Frontend ekosistemi: ücretsiz rehberler, proje şablonları, AI kuralları ve form bileşenleri.

Ürünler

  • ViraStack Start
  • ViraStack AI
  • ViraStack Mask
  • ViraStack Password
  • ViraStack Guide

Keşfet

  • Akademi
  • Hakkında
  • Marka
  • Destek

© 2026 ViraStack.

Açık kaynak · MIT

TanStack Query

04 / 12

queryOptions ve select

Sorgu ayarlarını queryOptions ile paylaşmak, query factory, tipli cache okuma ve select ile veriyi seçmek.

Custom Hook Neden Yetmez

Bir sorguyu birden fazla yerde kullanınca ilk akla gelen bir custom hook yazmaktır:

export function useProduct(id: number) {
  return useQuery({
    queryKey: ["products", "detail", id],
    queryFn: () => fetchProduct(id),
  });
}

TkDodo yıllarca bu kalıbı önerdi, sonra vazgeçti. Gerekçeleri:1

  • Hook yalnızca component'te ya da başka bir hook'ta çağrılır. Oysa aynı sorguya event handler'da prefetch için, route loader'da ya da sunucuda da ihtiyaç duyarsınız.

  • Burada paylaşılan şey mantık değil, ayardır. Hook bunun için gereğinden ağır bir araç.

  • Hook sizi useQuery'ye bağlar. Aynı sorguyu useSuspenseQuery ya da useQueries ile kullanmak istediğinizde işe yaramaz.

queryOptions

v5 ile gelen queryOptions çalışma anında hiçbir şey yapmaz; aldığı nesneyi geri döndürür. Değeri tip tarafındadır:23

// src/features/products/queries.ts
import { queryOptions } from "@tanstack/react-query";

export function productOptions(id: number) {
  return queryOptions({
    queryKey: ["products", "detail", id],
    queryFn: () => fetchProduct(id),
    staleTime: 30 * 1000,
  });
}

Aynı ayar her yerde çalışır:2

useQuery(productOptions(1));
useSuspenseQuery(productOptions(2));
useQueries({ queries: [productOptions(1), productOptions(2)] });
queryClient.query(productOptions(3));
queryClient.setQueryData(productOptions(4).queryKey, updatedProduct);

queryKey ile queryFn artık yan yana durur. TkDodo'nun deyişiyle "query key'i query function'dan ayırmak bir hataydı": anahtar, fonksiyonun bağımlılıklarını tanımlar; ikisi ayrı dosyalarda durunca senkron kalmaları zorlaşır.3 prefer-query-options lint kuralı bu kalıbı zorunlu kılar.4

Query Factory

Anahtar hiyerarşisini ve queryOptions'ı tek bir nesnede birleştirin. Yalnızca anahtar dönen girişler toplu invalidation için, queryOptions dönenler sorgu için:3

export const productQueries = {
  all: () => ["products"] as const,
  lists: () => [...productQueries.all(), "list"] as const,
  list: (category: string) =>
    queryOptions({
      queryKey: [...productQueries.lists(), category],
      queryFn: () => fetchProducts(category),
    }),
  details: () => [...productQueries.all(), "detail"] as const,
  detail: (id: number) =>
    queryOptions({
      queryKey: [...productQueries.details(), id],
      queryFn: () => fetchProduct(id),
      staleTime: 30 * 1000,
    }),
};
useQuery(productQueries.detail(id));
queryClient.invalidateQueries({ queryKey: productQueries.lists() });

Factory'yi özelliğin klasöründe tutun (ör. features/products/queries.ts). Klasör yapısı için Klasör yapısı dersine bakın.

Kullanıldığı Yerde Eklemek

Paylaşılan ayara parametre eklemeye başlarsanız custom hook'taki soruna geri dönersiniz. TkDodo'nun kuralı: en iyi soyutlamalar ayarlanabilir olmayanlardır. Ortak ayar sabit kalır, ek ayarları kullanıldığı yerde üstüne yazarsınız:1

const { data: productName } = useQuery({
  ...productQueries.detail(id),
  select: (product) => product.name,
});
//      ^? string | undefined

Tip çıkarımı bozulmaz; data'nın tipi select'in dönüşüne göre değişir.1

Tipli Cache Okuma

queryClient.getQueryData(["products", "detail", 1]) normalde unknown döner; kütüphane o anahtarın arkasında ne olduğunu bilemez. queryOptions'tan gelen anahtar ise queryFn'in dönüş tipiyle etiketlenir:35

const product = queryClient.getQueryData(productQueries.detail(1).queryKey);
//    ^? Product | undefined

fetchProduct'ın dönüş tipini değiştirdiğinizde bu satır da derleyicide hata verir; elle yazdığınız getQueryData<Product> vermezdi.3

İpucu

Tip çıkarımının çalışması için useQuery'ye generic vermeyin. queryFn'in dönüş tipini doğru yazın, gerisini kütüphane çıkarır. axios gibi any döndüren istemcileri tipli bir fonksiyona sarın.56

select ile Seçmek

select, cache'teki verinin bir parçasını ya da dönüştürülmüş halini döndürür. Cache'te tam cevap durur; component yalnızca select'in sonucuna abone olur ve yalnızca o sonuç değişince yeniden render edilir:78

const selectStockCount = (products: Product[]) =>
  products.filter((product) => product.stock > 0).length;

function StockBadge({ category }: { category: string }) {
  const { data: count } = useQuery({
    ...productQueries.list(category),
    select: selectStockCount,
  });
  return <span>{count} ürün stokta</span>;
}

select iki durumda yeniden çalışır: veri değişince ya da select fonksiyonunun referansı değişince. Satır içinde yazılan fonksiyon her render'da yenidir. Pahalı bir dönüşümse fonksiyonu component dışına çıkarın ya da props'a bağlıysa useCallback ile sarın.78

Uyarı

select içinde hata fırlatmayın. select yalnızca başarılı veriyle çalışır; hata durumu queryFn'in işidir.7

Render sayısını azaltan iki optimizasyon daha kendiliğinden açıktır: structural sharing değişmeyen veri parçalarının referansını korur, izlenen property'ler ise yalnızca kullandığınız alanlar (data, isFetching vb.) değişince render tetikler. İkincisini const { data, ...rest } = useQuery() gibi rest destructuring bozar.7

Kendin Dene

  • Projenizdeki bir custom hook'u queryOptions'a çevirin ve aynı ayarı hem useQuery hem queryClient.getQueryData ile kullanın.

  • getQueryData'ya queryOptions'tan gelen anahtarı verin ve dönüş tipine bakın. Sonra anahtarı elle yazın; tip ne oluyor?

  • Bir ürün listesinde yalnızca stoktaki ürün sayısını gösteren bir rozet yazın. React DevTools Profiler ile bir ürünün adı değiştiğinde rozetin render edilip edilmediğine bakın.

Detaylı Öğrenmek İçin

  • Yazı: TkDodo, "Creating Query Abstractions"; custom hook'tan queryOptions'a geçişin gerekçesi.1

  • Yazı: TkDodo, "The Query Options API"; DataTag, query factory ve anahtar ile fonksiyonu birlikte tutmak.3

  • Yazı: TkDodo, "React Query Selectors, Supercharged"; select'i tiplemek ve pahalı dönüşümleri memoize etmek.8

Kaynaklar

  • 1MakaleTkDodo

    : — 23 Şubat 2026. Custom hook'ların sınırları ve `queryOptions`'ı kullanıldığı yerde genişletmek.

  • 2DokümanTanStack Query: Query Options

    `queryOptions`'ı farklı hook'larda ve `QueryClient` metotlarında paylaşmak.

  • 3MakaleTkDodo

    : — 17 Ocak 2024. `DataTag`, query factory ve anahtar ile fonksiyonu birlikte tutmak.

  • 4DokümanTanStack Query: prefer-query-options

    `queryKey` ve `queryFn`'i `queryOptions` ile birlikte tanımlamaya zorlayan kural.

  • 5DokümanTanStack Query: TypeScript

    Tip çıkarımı, `queryOptions` ile tipli `getQueryData` ve global hata tipi.

  • 6MakaleTkDodo

    : — 16 Mayıs 2021. Generic vermek yerine çıkarıma güvenmek.

  • 7DokümanTanStack Query: Render Optimizations

    Structural sharing, izlenen property'ler ve `select`'in memoization kuralları.

  • 8MakaleTkDodo

    : — 4 Ağustos 2025. İnce taneli abonelik ve `select`'i stabil tutmak.

Bu sayfayı düzenle

Son güncelleme tarihi: 21 Ekim 2026

Önceki adım03. useQuery ve query keySonraki adım05. Cache ve stale time