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

07 / 12

Optimistic update

Sunucuyu beklemeden arayüzü güncellemek: variables ile, cache ile, geri alma ve eşzamanlı mutation'lar.

Ne Zaman Gerekir

Optimistic update, mutation'ın başarılı olacağını varsayıp sonucu sunucudan cevap gelmeden göstermektir. Cevap gelince gerçek veriyle değiştirirsiniz; hata olursa geri alırsınız.1

TkDodo bu tekniğin fazla kullanıldığını düşünür. Önerdiği iki koşul:2

  • Mutation nadiren başarısız olmalı. Geri alma kullanıcı için kötü bir deneyimdir.

  • Anında geri bildirim gerçekten gerekmeli. Favori butonu, yapılacaklar kutucuğu gibi küçük değişiklikler iyi adaylardır.

Kapanan bir dialog ya da başka sayfaya yönlendiren bir form iyimser yapılırsa hata durumunda geri almak çok zordur. Sunucunun mantığını istemcide taklit etmek de kolay değildir: yeni eklenen kaydın id'si nereden gelecek, sıralı listede nereye girecek?2 Çoğu durumda butonu devre dışı bırakıp yükleme göstermek yeter.

Doküman iki yol önerir: arayüz üzerinden ve cache üzerinden.1

Arayüz Üzerinden: variables

En basit yol cache'e hiç dokunmamaktır. Mutation sürerken variables hâlâ elinizdedir; geçici bir satır olarak gösterirsiniz:1

function ReviewList({ productId }: { productId: number }) {
  const queryClient = useQueryClient();
  const reviews = useQuery(reviewQueries.list(productId));

  const addReview = useMutation({
    mutationFn: (text: string) => postReview(productId, text),
    onSettled: () =>
      queryClient.invalidateQueries({ queryKey: reviewQueries.list(productId).queryKey }),
  });

  return (
    <ul>
      {reviews.data?.map((review) => <li key={review.id}>{review.text}</li>)}
      {addReview.isPending && <li style={{ opacity: 0.5 }}>{addReview.variables}</li>}
      {addReview.isError && (
        <li>
          {addReview.variables}{" "}
          <button onClick={() => addReview.mutate(addReview.variables)}>Tekrar dene</button>
        </li>
      )}
    </ul>
  );
}

onSettled invalidation'ın Promise'ini döndürdüğü için mutation, yenilenen liste gelene kadar pending kalır. Böylece soluk satır kaybolduğu anda gerçek satır yerini alır. Hata olursa variables silinmez; "Tekrar dene" butonu aynı değeri yeniden gönderir.1 Geri alma kodu yazmanız gerekmez.

Mutation ile liste farklı component'lerdeyse mutation'a bir mutationKey verin ve listede useMutationState ile okuyun:1

const pendingReviews = useMutationState({
  filters: { mutationKey: ["add-review", productId], status: "pending" },
  select: (mutation) => mutation.state.variables as string,
});

useMutationState bir dizi döndürür; aynı anda birden fazla yorum gönderiliyor olabilir.1

Cache Üzerinden: onMutate

İyimser değer ekranın birden fazla yerinde görünecekse (liste, sayaç, detay) cache'i doğrudan güncelleyin. Kalıp dört adımdır:1

useMutation({
  mutationFn: toggleFavorite,
  onMutate: async (productId: number, context) => {
    const queryKey = productQueries.detail(productId).queryKey;

    // 1. Uçuştaki istekleri iptal et; iyimser değerin üstüne yazmasınlar
    await context.client.cancelQueries({ queryKey });

    // 2. Geri almak için şimdiki değeri sakla
    const previous = context.client.getQueryData(queryKey);

    // 3. İyimser değeri yaz
    context.client.setQueryData(queryKey, (old) =>
      old ? { ...old, isFavorite: !old.isFavorite } : old,
    );

    return { previous, queryKey };
  },
  // 4a. Hata olursa geri al
  onError: (_error, _productId, onMutateResult, context) => {
    if (onMutateResult) {
      context.client.setQueryData(onMutateResult.queryKey, onMutateResult.previous);
    }
  },
  // 4b. Her durumda sunucuyla eşitle
  onSettled: (_data, _error, productId, _onMutateResult, context) =>
    context.client.invalidateQueries({ queryKey: productQueries.detail(productId).queryKey }),
});

onMutate'in döndürdüğü nesne diğer callback'lere onMutateResult olarak gelir. context.client aynı QueryClient'tır; component içinde useQueryClient() çağırmanıza gerek kalmaz.13 queryOptions'tan gelen anahtarı kullandığınız için getQueryData ve setQueryData'nın tipi de doğru çıkar.

İki Yolun Karşılaştırması

variables ileCache ile
Kod miktarıAzFazla
Geri almaGerekmezonError'da elle
İyimser değer nerede görünürYalnızca mutation'ı okuyan yerdeSorguyu kullanan her yerde
Ne zamanDeğer tek bir yerde görünüyorsaBirden fazla ekran parçası etkileniyorsa

Doküman da aynı sonuca varır: tek bir yer için variables, birden fazla yer için cache.1

Eşzamanlı Mutation'lar

Kullanıcı favori butonuna art arda üç kez basarsa üç mutation aynı anda çalışır. TkDodo burada bir tutarsızlık penceresi tarif eder: ilk mutation bitip invalidation başlattığında, ikinci mutation henüz bitmemişse ve yenileme ondan hızlı gelirse arayüz bir anlığına eski değere döner.4

İki önlem bu titremeyi giderir:4

  • onMutate'te cancelQueries: mutation başladığında uçuştaki sorgular iptal edilir.

  • onSettled'da yalnızca son mutation invalidation yapar. Kendi mutation'ınız hâlâ sayıldığı için kontrol === 1 ile yapılır:

useMutation({
  mutationKey: ["toggle-favorite"],
  mutationFn: toggleFavorite,
  onSettled: (_data, _error, productId, _onMutateResult, context) => {
    if (context.client.isMutating({ mutationKey: ["toggle-favorite"] }) === 1) {
      return context.client.invalidateQueries({
        queryKey: productQueries.detail(productId).queryKey,
      });
    }
  },
});

Bu kontrolü useIsMutating hook'u ile değil, isMutating ile o anda yapın; hook'un değeri callback'te eski kalabilir.4

React'in useOptimistic'i ile Farkı

React 19'un useOptimistic'i bir Action süresince React state'ine geçici değer yazar (bkz. Actions ve async güncellemeler). Server state'iniz TanStack Query cache'indeyse iyimser değeri de orada tutun; aynı verinin iki ayrı iyimser kopyası senkron dışı kalabilir.

Kendin Dene

  • Favori butonunu önce iyimser olmadan yazın ve API'ye 1 saniye gecikme ekleyin. Butona basınca ne hissediyorsunuz?

  • variables yolunu bir yorum listesine uygulayın. API'yi her üçüncü istekte hata verecek şekilde değiştirin ve "Tekrar dene"yi deneyin.

  • Cache yolunu kurun, cancelQueries satırını silin. Sekmeye dönüp hemen butona basarak titremeyi yakalamaya çalışın.

Detaylı Öğrenmek İçin

  • Resmi doküman: "Optimistic Updates"; variables, useMutationState ve cache ile geri alma örnekleri.1

  • Yazı: TkDodo, "Concurrent Optimistic Updates in React Query"; tutarsızlık penceresi, iptal ve sınırlı invalidation.4

  • Yazı: TkDodo, "Mastering Mutations in React Query"; iyimser güncellemenin ne zaman gerekmediği.2

Kaynaklar

  • 1DokümanTanStack Query: Optimistic Updates

    Arayüz ve cache yolu, `useMutationState`, geri alma ve hangisinin ne zaman kullanılacağı.

  • 2MakaleTkDodo

    : — 9 Ekim 2021. İyimser güncellemenin fazla kullanılması ve zor geri alınan durumlar.

  • 3DokümanTanStack Query: Mutations

    Callback imzaları, `onMutateResult` ve `context.client`.

  • 4MakaleTkDodo

    : — 28 Nisan 2025. Tutarsızlık penceresi, `cancelQueries` ve `isMutating` ile sınırlı invalidation.

Bu sayfayı düzenle

Son güncelleme tarihi: 21 Ekim 2026

Önceki adım06. Mutation ve invalidationSonraki adım08. Bağımlı ve paralel sorgular