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.