← Tüm yazılar

React hook ile dosya yükleme durum makinesi

Dosya yüklerken kullanıcı ikinci dosyayı seçebilir; ilk isteğin geç bitmesi yeni seçimin üstüne yazmamalı. Küçük bir sıra numarası bu yarışın ekran tarafını kontrol altında tutar.

t.Turgay
React hook ile dosya yükleme durum makinesi

Dosya A yüklenirken kullanıcı B'yi seçti. B önce tamamlandı; biraz sonra A'nın yanıtı gelip ekrandaki yeni dosyanın URL'sini ezdi. Bu hata yükleme servisinin çalışmamasından değil, iki başarılı isteğin yanlış sırada UI'a uygulanmasından çıkabilir. Eski portfolio denememdeki storage hook yaklaşımını bu yarış üzerinden açıklıyorum.

Sağlayıcıyı hook'un içine kilitlememek

useUpload, dışarıdan send fonksiyonu alıyor. Bu adaptör bir dosyayı gönderip URL döndürüyor; hook'un işi sağlayıcının tüm SDK ayrıntılarını bilmek değil, ekranın yükleme durumunu yönetmek. React istemci projesindeki .tsx dosyasında kullanılabilecek bağımsız örnek aşağıda. Gerçek endpoint, kimlik ve dosya kabul kuralları ayrıca gerekir.

import {useRef,useState,useEffect} from 'react';
export function useUpload(send:(file:File)=>Promise<string>){
  const [state,setState]=useState<{status:string;url?:string}>({status:'idle'});
  const sequence=useRef(0);
  useEffect(()=>()=>{sequence.current++;},[]);
  async function upload(file:File){
    const current=++sequence.current;
    setState({status:'uploading'});
    try{
      const url=await send(file);
      if(current===sequence.current)setState({status:'ready',url});
    }catch{
      if(current===sequence.current)setState({status:'error'});
    }
  }
  return {state,upload};
}

Her çağrı sequence sayacını artırıp o anki değeri current içine alıyor. Yanıt geldiğinde yalnız current hâlâ güncelse state değişiyor. B ikinci çağrı olduğundan A'nın geç yanıtı karşılaştırmadan geçemez. Aynı kontrol catch içinde de var; eski bir hata yeni dosyanın başarılı durumunu bozamıyor.

Durumları tiplerle de sınırlamak

Mevcut kısa kod status alanını string olarak tutuyor. idle, uploading, ready ve error bilinen kümesini TypeScript'e de anlatmak daha sağlamdır. Aşağıdaki tip, hook'taki useState tanımının yerine kullanılabilir; ready dışında URL okumak isteyen tüketiciyi de daha açık dallanmaya yönlendirir.

type UploadState =
  | {status: 'idle'}
  | {status: 'uploading'}
  | {status: 'ready'; url: string}
  | {status: 'error'};

const [state, setState] = useState<UploadState>({status: 'idle'});

Böylece uploading durumunda yanlışlıkla eski URL'yi zorunlu veri sanmam. Gerçek üründe kullanıcıya gösterilecek güvenli hata ve gerekirse progress alanı eklenebilir; her durumun bütün alanları optional taşıdığı büyük bir nesne yerine ayrık durumlar akışı daha iyi anlatır.

Sayaç iptal değildir

sequence yalnız eski yanıtın ekrana uygulanmasını engeller. A dosyası sunucuda yüklenmeye ve saklanmaya devam edebilir. AbortController gibi iptal desteği gerekiyorsa send adaptörünün bu sözleşmeyi açıkça desteklemesi gerekir. İsteği iptal etmek de sunucuda oluşmuş nesneyi otomatik geri almış olmak değildir.

Birden çok dosyayı birlikte kabul eden bir ekran için bu “yalnız son istek kazanır” modeli uygun olmayabilir. O durumda her dosyanın kendi kimliği ve durum kaydı tutulur. Tek önizleme alanı için doğru olan yarış çözümünü bütün yükleme ürünlerine genellemem.

Unmount sonrası neden sayaç değişiyor?

Effect cleanup, bileşen kaldırılınca eski isteğin current değerini geçersiz kılar. Geliştirmede Strict Mode'un setup/cleanup denemeleri düşünülerek bu davranış idempotent kalmalı. Hook'u kullanan bileşen ortadan kalkmışken geç yanıtın artık kullanılmaması gerekir. Bu yine ağ işinin sona erdiği anlamına gelmez.

Yükleme ile içeriğe bağlama iki adım olabilir

Dosyanın URL'sini almak, onu bir makalenin kapağı olarak kaydetmekle aynı işlem değildir. Dosya yüklenmiş ama makale kaydı başarısızsa sahipsiz nesne oluşabilir. Geçici dosya kimliği, içerikle bağlandı durumu veya kontrollü temizleme politikası düşünülebilir. Kullanıcıya “hazır” gösterirken hangi adımın tamamlandığı açık olmalı.

Sunucu yine dosya tipi/boyutu ve kullanıcının yetkisini doğrular. Hook'un gönderim öncesi kontrolleri yalnız deneyim içindir; depolama anahtarları ve servis sırları tarayıcıya taşınmaz.

Yarışı kontrollü Promise'lerle test etmek

A ve B için elle tamamlanan iki Promise kullanırım. Önce B'yi, sonra A'yı çözünce son durum B'nin ready sonucu olmalı. A'yı reject etmek de B'yi error'a çevirmemeli. Ardından en güncel isteğin hatası ve bileşen kaldırılması sınanır. Bu testler gerçek büyük dosya yüklemeden, doğrudan asenkron durum sözleşmesini kontrol eder.

React effect cleanup; TypeScript ayrık birleşimler.

✳ Bu yörüngenin sonuna geldin.
Diğer yazıları keşfet →
turgay.Biraz kod, biraz piksel, bolca merak.© 2026 · Dünya’dan sevgilerle ✳