38982-vm/app-9xzmfic2e4g1/docs/error-handling-quick-reference.md
2026-03-04 19:36:44 +00:00

8.3 KiB
Raw Permalink Blame History

Error Handling Quick Reference

Hızlı Başlangıç

1. Basit API Çağrısı

import { parseApiError, logError } from '@/utils/errorHandler';

try {
  const result = await api.someFunction();
  // Başarılı
} catch (err: any) {
  const apiError = parseApiError(err);
  logError(apiError, { context: 'some_operation' });
  toast.error('İşlem başarısız', {
    description: apiError.userMessage,
  });
}

2. Retry ile API Çağrısı

import { retryWithBackoff } from '@/utils/retryWithBackoff';
import { parseApiError } from '@/utils/errorHandler';

const result = await retryWithBackoff(
  async () => api.someFunction(),
  {
    maxRetries: 3,
    initialDelay: 1000,
    onRetry: (attempt) => {
      toast.warning(`Yeniden deneniyor... (${attempt}/3)`);
    },
  }
);

3. Timeout ile API Çağrısı

import { withTimeout } from '@/utils/retryWithBackoff';

const result = await withTimeout(
  api.someFunction(),
  30000, // 30 saniye
  new Error('İşlem 30 saniyede tamamlanamadı')
);

4. Tam Özellikli Örnek

import { useState, useRef } from 'react';
import { parseApiError, logError, logSuccess } from '@/utils/errorHandler';
import { retryWithBackoff, withTimeout } from '@/utils/retryWithBackoff';
import { toast } from 'sonner';

function MyComponent() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<ApiError | null>(null);
  const [retryCount, setRetryCount] = useState(0);
  const startTimeRef = useRef<number>(0);

  const handleSubmit = async () => {
    setLoading(true);
    setError(null);
    startTimeRef.current = Date.now();

    try {
      const result = await retryWithBackoff(
        async () => {
          return await withTimeout(
            api.someFunction(),
            30000,
            new Error('İşlem 30 saniyede tamamlanamadı')
          );
        },
        {
          maxRetries: 3,
          initialDelay: 1000,
          onRetry: (attempt, err) => {
            setRetryCount(attempt);
            const apiError = parseApiError(err);
            toast.warning(`Yeniden deneniyor... (${attempt}/3)`, {
              description: apiError.userMessage,
            });
          },
        }
      );

      // Başarı
      const duration = Date.now() - startTimeRef.current;
      logSuccess('some_operation', duration);
      toast.success('İşlem başarılı!');
      
    } catch (err: any) {
      const apiError = parseApiError(err);
      logError(apiError, { retryCount });
      setError(apiError);
      toast.error('İşlem başarısız', {
        description: apiError.userMessage,
      });
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      {error && (
        <Alert variant="destructive">
          <AlertCircle className="h-5 w-5" />
          <AlertTitle>{error.type}</AlertTitle>
          <AlertDescription>{error.userMessage}</AlertDescription>
        </Alert>
      )}
      
      <Button onClick={handleSubmit} disabled={loading}>
        {loading ? 'Yükleniyor...' : 'Gönder'}
      </Button>
    </div>
  );
}

Hata Tipleri ve Mesajları

Tip Mesaj Retry?
network İnternet bağlantınızı kontrol edin
timeout İşlem uzun sürdü, lütfen tekrar deneyin
validation Gönderilen bilgiler geçersiz
server Sunucu hatası oluştu
ratelimit Çok fazla istek gönderildi
unknown Beklenmeyen bir hata oluştu

Retry Stratejisi

Exponential Backoff

Deneme 1: Hemen
Deneme 2: 1 saniye sonra
Deneme 3: 2 saniye sonra
Deneme 4: 4 saniye sonra

Özelleştirme

{
  maxRetries: 3,           // Maksimum deneme sayısı
  initialDelay: 1000,      // İlk bekleme süresi (ms)
  maxDelay: 30000,         // Maksimum bekleme süresi (ms)
  backoffMultiplier: 2,    // Çarpan (exponential)
  shouldRetry: (err) => {  // Retry koşulu
    return isRetryableError(err);
  },
  onRetry: (attempt, err) => {  // Retry callback
    console.log(`Retry ${attempt}`);
  },
}

Loading States

Basit Loading

const [loading, setLoading] = useState(false);

<Button disabled={loading}>
  {loading ? (
    <>
      <Loader2 className="mr-2 h-5 w-5 animate-spin" />
      Yükleniyor...
    </>
  ) : (
    'Gönder'
  )}
</Button>

Multi-Step Loading

const LOADING_STEPS = [
  { label: 'Hazırlanıyor...', progress: 0 },
  { label: 'İşleniyor...', progress: 50 },
  { label: 'Tamamlanıyor...', progress: 90 },
];

const [loadingStep, setLoadingStep] = useState(0);

<Card>
  <p>{LOADING_STEPS[loadingStep]?.label}</p>
  <Progress value={LOADING_STEPS[loadingStep]?.progress} />
</Card>

Form Recovery

Otomatik Kayıt

useEffect(() => {
  const subscription = form.watch((value) => {
    localStorage.setItem('form_draft', JSON.stringify(value));
  });
  return () => subscription.unsubscribe();
}, [form]);

Otomatik Kurtarma

useEffect(() => {
  const savedData = localStorage.getItem('form_draft');
  if (savedData) {
    form.reset(JSON.parse(savedData));
    toast.info('Önceki formunuz geri yüklendi');
  }
}, [form]);

İptal (Cancel)

AbortController

const abortControllerRef = useRef<AbortController | null>(null);

// Başlat
abortControllerRef.current = new AbortController();

// İptal et
const handleCancel = () => {
  if (abortControllerRef.current) {
    abortControllerRef.current.abort();
  }
  setLoading(false);
  toast.info('İşlem iptal edildi');
};

// Cleanup
useEffect(() => {
  return () => {
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
    }
  };
}, []);

Logging

Error Logging

import { logError } from '@/utils/errorHandler';

logError(apiError, {
  operation: 'generate_itinerary',
  userId: user?.id,
  timestamp: Date.now(),
});

Success Logging

import { logSuccess } from '@/utils/errorHandler';

const startTime = Date.now();
// ... API call
const duration = Date.now() - startTime;

logSuccess('generate_itinerary', duration, {
  userId: user?.id,
  itemCount: result.length,
});

Error History

// LocalStorage'dan error history al
const errorHistory = JSON.parse(
  localStorage.getItem('error_history') || '[]'
);

console.log('Son 10 hata:', errorHistory);

Toast Bildirimleri

Başarı

toast.success('İşlem başarılı!', {
  description: 'Rotanız oluşturuldu',
});

Hata

toast.error('İşlem başarısız', {
  description: apiError.userMessage,
});

Uyarı

toast.warning('Yeniden deneniyor...', {
  description: `Deneme ${attempt}/3`,
});

Bilgi

toast.info('İşlem iptal edildi');

Best Practices

Yapılması Gerekenler

  • Her API çağrısında parseApiError() kullan
  • Kullanıcı dostu Türkçe mesajlar göster
  • Retry edilebilir hataları otomatik retry et
  • Loading states ile kullanıcıyı bilgilendir
  • Form verilerini localStorage'a kaydet
  • Hataları logla (console + localStorage)
  • Success metriklerini kaydet

Yapılmaması Gerekenler

  • Genel "Bir hata oluştu" mesajları gösterme
  • Validation hatalarını retry etme
  • Hassas bilgileri loglama
  • Stack trace'leri kullanıcıya gösterme
  • Sonsuz retry döngüsü oluşturma
  • Memory leak'e neden olma (cleanup yap)

Debugging

Console'da Error History

const history = localStorage.getItem('error_history');
console.log('Error History:', JSON.parse(history || '[]'));

Performance Monitoring

const startTime = performance.now();
await api.someFunction();
const duration = performance.now() - startTime;
console.log(`Duration: ${duration}ms`);

Network Tab

  • Chrome DevTools → Network
  • Filter: XHR/Fetch
  • Check: Status, Time, Response

Test

Demo Sayfası

/error-demo

Manuel Test

  1. Network hatası: DevTools → Network → Offline
  2. Timeout: API'yi geciktir
  3. Validation: Geçersiz veri gönder
  4. Server: Backend'i kapat
  5. Rate limit: Çok fazla istek gönder

Kaynaklar