Error Boundaries — React

Bayangin app banking — pas user lihat saldo, satu komponen lempar error karena data corrupt. Tanpa error boundary, ENTIRE APP CRASH — white screen, user gak…

Bayangin app banking — pas user lihat saldo, satu komponen <TransactionList> lempar error karena data corrupt. Tanpa error boundary, ENTIRE APP CRASH — white screen, user gak bisa logout, gak bisa lihat info lain. Dengan error boundary, error di-catch, fallback UI muncul ("Daftar transaksi error, coba refresh"), sisa app tetap jalan. Error Boundaries adalah komponen yang nge-catch JavaScript error di render tree komponen children-nya, prevent app crash total. WAJIB ada di production app — minimal di top level.

💡 Production must-have. Tiap React app yang serius punya minimum 1 error boundary di root. Library populer: react-error-boundary — kasih hook + component pattern + reset functionality, cleaner dari class component manual. Lebih mudah di-test.

Class Component (Built-in)

class ErrorBoundary extends React.Component {
  state = { hasError: false, error: null }

  static getDerivedStateFromError(error) {
    return { hasError: true, error }
  }

  componentDidCatch(error, info) {
    console.error("Error caught:", error, info)
    // Kirim ke service: Sentry.captureException(error)
  }

  render() {
    if (this.state.hasError) {
      return (
        <div className="error-fallback">
          <h2>Ada yang salah</h2>
          <p>{this.state.error.message}</p>
          <button onClick={() => this.setState({ hasError: false })}>
            Coba Lagi
          </button>
        </div>
      )
    }
    return this.props.children
  }
}

// Pemakaian
<ErrorBoundary>
  <App />
</ErrorBoundary>

Library: react-error-boundary (Recommended)

import { ErrorBoundary } from "react-error-boundary"

function ErrorFallback({ error, resetErrorBoundary }) {
  return (
    <div role="alert">
      <p>Error: {error.message}</p>
      <button onClick={resetErrorBoundary}>Coba Lagi</button>
    </div>
  )
}

<ErrorBoundary
  FallbackComponent={ErrorFallback}
  onError={(error, info) => Sentry.captureException(error)}
  onReset={() => window.location.reload()}
>
  <App />
</ErrorBoundary>

Granular Boundaries — Best Practice

function App() {
  return (
    <ErrorBoundary fallback={<AppErrorPage />}>  {/* root catch */}
      <Layout>
        <Sidebar />
        <ErrorBoundary fallback={<MainErrorPage />}>  {/* main content area */}
          <Routes>
            <Route path="/transactions" element={
              <ErrorBoundary fallback={<TransactionsError />}>  {/* halaman tertentu */}
                <Transactions />
              </ErrorBoundary>
            } />
          </Routes>
        </ErrorBoundary>
      </Layout>
    </ErrorBoundary>
  )
}

Layered: error di Transactions cuma kena fallback Transactions, app + sidebar tetap jalan.

Apa yang TIDAK ditangkap

Event handleronClick={() => { throw new Error() }} lolos error boundary. Pakai try/catch manual. ❌ Async codesetTimeout(() => { throw err }, 100) lolos. Pakai try/catch di async function. ❌ Server-side rendering — error di SSR di-handle berbeda. ❌ Error di error boundary itu sendiri — error boundary gak self-heal, parent boundary yang nge-catch.

// Event handler — pakai try/catch
const handleClick = () => {
  try {
    riskyOp()
  } catch (e) {
    setError(e.message)
  }
}

// Async — pakai try/catch
async function loadData() {
  try {
    const data = await fetch(...)
  } catch (e) {
    setError(e.message)
  }
}

🎭 Analogi sehari-hari

Error Boundary itu kayak sekat tahan api di gedung tinggi. Pas ada kebakaran di lantai 5, sekat nyegah api naik ke lantai 6-50 — orang di lantai atas tetap aman, evacuation di lantai 5 doang. Tanpa sekat (no error boundary), satu kebakaran kecil = seluruh gedung kebakar (entire app crash). Granular boundaries = banyak sekat di lantai berbeda. Error di Transactions section = cuma section itu yang "kebakaran", Sidebar + Header + Footer tetap fungsional. Logging error juga otomatis (componentDidCatch / onError) = tahu error apa, di mana, kapan terjadi.

⚠️ Jebakan yang sering ditemui

🎯 Tempat pasang error boundary?

  • Root level (App) → catch-all fallback "Aplikasi error, refresh" (WAJIB)
  • Per route page → fallback per halaman (page error gak crash navigation)
  • Per data-heavy widget (chart, table, complex form) → fallback per widget
  • Per critical section (auth, payment) → custom fallback dengan retry
  • Around Suspense boundary → handle async loading + error

Aturan: minimal 1 di root + 1 per route. Tambah lebih granular di section yang prone error (data fetching, third-party widget).

TL;DR: Error Boundary = komponen yang nge-catch error di render children, tampilin fallback UI biar app gak crash total. Class component pattern (atau pakai library react-error-boundary). WAJIB ada di production app — minimum 1 di root. Granular: per route + per critical section. TIDAK nangkep: event handler error, async error, SSR error — pakai try/catch manual. Logging via componentDidCatch/onError ke Sentry/Bugsnag.

Yang akan kamu pelajari