Error Boundaries

React'te render sırasındaki hataları yakalayan class component'ler: getDerivedStateFromError, componentDidCatch, error boundary'lerin kapsamı ve yakalamadıkları, basit örneklerle.

İleri 13 dk
EN

Error Boundaries

Bu derste, kursta İLK KEZ bir class component göreceğiz. React'te error boundary'leri (hata sınırları) hook'larla yazmanın bir yolu yok -- yalnızca class component'ler kullanılarak yazılabiliyor. Şimdiye kadar öğrendiğin her şey (hooks, state, props) fonksiyon component'lerle ilgiliydi; bu, istisnai ve dar kapsamlı bir konu.

Temel Bir Error Boundary Yazmak

Bir component render sırasında hata fırlattığında, React normalde TÜM uygulamayı "unmount" eder (boş bir ekran gösterir). Error boundary, bunu önler:

import { Component } from "react";

// ÖNEMLİ: Error boundary'ler, bu kursta gördüğümüz İLK class component.
// React'te error boundary'leri hook'larla (fonksiyon component'lerle)
// yazmanın bir yolu YOK -- yalnızca class component'ler
// `static getDerivedStateFromError` ile bunu yapabiliyor. Bu yüzden bu
// TEK konuda, istisnai olarak bir class component kullanıyoruz.
class BasicErrorBoundaryExample extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  // Bir child component render sırasında hata FIRLATTIĞINDA, React bu
  // metodu çağırır -- döndürdüğü değer, yeni state olur.
  static getDerivedStateFromError() {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <p>Something went wrong.</p>;
    }

    return this.props.children;
  }
}

static getDerivedStateFromError(), bir child hata fırlattığında React tarafından çağrılır -- döndürdüğü değer yeni state olur. render() metodu, hasError durumuna göre ya normal children'ı ya da bir fallback mesajı gösterir.

componentDidCatch ile Hatayı Loglamak

getDerivedStateFromError, YALNIZCA fallback UI'ı göstermek için kullanılır -- hatayı bir yere göndermek (loglamak) için ayrı bir metot gerekir:

import { Component } from "react";

class ComponentDidCatchExample extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  // getDerivedStateFromError, fallback UI'ı GÖSTERMEK için kullanılır;
  // componentDidCatch ise hatayı bir yere GÖNDERMEK (örneğin bir loglama
  // servisine) için kullanılır -- ikisi birlikte çalışabilir.
  componentDidCatch(error, errorInfo) {
    console.error("Caught an error:", error, errorInfo.componentStack);
  }

  render() {
    if (this.state.hasError) {
      return <p>Something went wrong.</p>;
    }

    return this.props.children;
  }
}

componentDidCatch(error, errorInfo), hatanın kendisini VE errorInfo.componentStack'i (hatanın hangi component'te olduğunu gösteren bir "yığın izi") alır -- gerçek uygulamalarda burada genellikle bir hata izleme servisine (Sentry gibi) bir istek atılır.

Error Boundary Kullanmak

Bir error boundary'i, hata fırlatabilecek component'leri sarmalamak için kullanırız:

import { Component, useState } from "react";

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <p>Something went wrong.</p>;
    }

    return this.props.children;
  }
}

function BuggyCounter({ count }) {
  if (count === 3) {
    // Render sırasında bilinçli olarak bir hata fırlatıyoruz -- gerçek bir
    // uygulamada bu, beklenmedik bir `undefined.someProperty` gibi bir hata
    // olurdu.
    throw new Error("Count reached 3!");
  }

  return <p>Count: {count}</p>;
}

function UsingErrorBoundaryExample() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      {/* ErrorBoundary, İÇİNDEKİ herhangi bir component render sırasında
          hata fırlatırsa, o hatayı YAKALAR ve normal render'ı fallback
          UI'la DEĞİŞTİRİR -- BuggyCounter'ın kendisi hatayı yönetmek
          zorunda değil. */}
      <ErrorBoundary>
        <BuggyCounter count={count} />
      </ErrorBoundary>
    </div>
  );
}

BuggyCounter, count === 3 olduğunda bilinçli olarak bir hata fırlatıyor -- ErrorBoundary bunu yakalayıp normal render'ı fallback UI'la değiştiriyor. BuggyCounter'ın kendisi hatayı yönetmek zorunda DEĞİL, bu error boundary'nin işi.

Error Boundary'lerin Kapsamı

Birden fazla, KÜÇÜK error boundary kullanmak, tek bir büyük boundary'den genellikle daha iyidir:

import { Component } from "react";

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <p>{this.props.fallbackText}</p>;
    }

    return this.props.children;
  }
}

function BuggyWidget() {
  throw new Error("This widget is broken!");
}

function ErrorBoundaryScopeExample() {
  return (
    <div>
      {/* İki AYRI ErrorBoundary, iki AYRI bölümü sarmalıyor -- Sidebar
          çökse bile, MainContent bundan ETKİLENMEZ, kendi ErrorBoundary'si
          İÇİNDE kalır. Tek bir büyük ErrorBoundary kullansaydık, herhangi
          bir hata TÜM sayfayı "Something went wrong" mesajına
          çevirebilirdi. */}
      <ErrorBoundary fallbackText="Sidebar failed to load.">
        <BuggyWidget />
      </ErrorBoundary>
      <ErrorBoundary fallbackText="Main content failed to load.">
        <p>Main content (still works fine)</p>
      </ErrorBoundary>
    </div>
  );
}

İki ayrı ErrorBoundary, iki ayrı bölümü sarmalıyor -- biri çökse bile, diğeri bundan ETKİLENMİYOR. Tek bir büyük boundary kullansaydık, herhangi bir hata TÜM sayfayı fallback mesajına çevirebilirdi.

Error Boundary'lerin Yakalamadığı Hatalar

Error boundary'lerin bir sınırı var -- her tür hatayı yakalamazlar:

import { Component, useState } from "react";

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <p>Something went wrong.</p>;
    }

    return this.props.children;
  }
}

function EventHandlerExample() {
  function handleClick() {
    // Error boundary'ler yalnızca RENDER sırasındaki hataları yakalar --
    // bir event handler İÇİNDE (bir onClick gibi) fırlatılan bir hata,
    // error boundary tarafından YAKALANMAZ. Bunun için normal try/catch
    // kullanmak gerekir.
    try {
      throw new Error("Button click failed!");
    } catch (error) {
      console.error("Caught manually:", error.message);
    }
  }

  return <button onClick={handleClick}>Click me</button>;
}

function WhatErrorBoundariesDontCatchExample() {
  const [showInfo, setShowInfo] = useState(false);

  return (
    <div>
      {/* ErrorBoundary burada EventHandlerExample'ı sarmalıyor -- ama
          içindeki onClick hatası yine de YAKALANMAYACAK, çünkü o bir
          event handler'da oluyor, render sırasında değil. */}
      <ErrorBoundary>
        <EventHandlerExample />
      </ErrorBoundary>
      <button onClick={() => setShowInfo(true)}>Show limitations</button>
      {showInfo && (
        <p>
          Error boundaries do NOT catch: event handler errors, errors in
          asynchronous code (setTimeout, fetch callbacks), errors during
          server-side rendering, or errors thrown in the boundary itself.
        </p>
      )}
    </div>
  );
}

Error boundary'ler yalnızca RENDER sırasındaki hataları yakalar -- event handler'lardaki (onClick gibi), asenkron kod içindeki (setTimeout, fetch callback'leri), sunucu tarafı render'daki, ya da boundary'nin KENDİSİNDE fırlatılan hataları YAKALAMAZLAR. Event handler'lardaki hatalar için normal try/catch kullanılır.

Özet ve Terimler Sözlüğü

Bir error boundary, static getDerivedStateFromError (fallback UI göstermek için) ve isteğe bağlı componentDidCatch (hatayı loglamak için) tanımlayan bir class component'tir -- React'te bunun hook karşılığı yoktur. Bir error boundary, İÇİNDEKİ herhangi bir component'in render sırasında fırlattığı hatayı yakalar ve normal render'ı bir fallback UI'la değiştirir. Birden fazla küçük boundary kullanmak, bir bölümdeki hatanın diğerlerini etkilemesini önler. Error boundary'ler event handler'lardaki, asenkron koddaki, ya da kendi içindeki hataları YAKALAMAZ.

Terimler Sözlüğü

Error Boundary (Hata Sınırı) — İçindeki component'lerin render sırasında fırlattığı hataları yakalayıp bir fallback UI gösteren class component.

Fallback UI — Bir hata (ya da yükleme durumu) sırasında, normal içerik yerine gösterilen alternatif arayüz.