Error Boundary — xatolarni ushlash, fallback UI

~15 daq · React · Ilg'or mavzular

Muammodan boshlaylik

Mahsulot sahifasida "Sharhlar" bloki bor. Server bitta sharhni noto'g'ri formatda qaytardi:

function Sharh({ sharh }) {
  return <p>{sharh.muallif.ism}: {sharh.matn}</p>;   // muallif — null
}
TypeError: Cannot read properties of null (reading 'ism')

Natija: butun sahifa oq bo'lib qoldi. Header ham, mahsulot rasmi ham, "Savatga" tugmasi ham — hammasi yo'qoldi. Bitta sharhdagi xato tufayli foydalanuvchi mahsulotni sotib ololmaydi.

Bu React ning standart xatti-harakati: render paytida xato bo'lsa, React butun daraxtni olib tashlaydi — chunki buzilgan UI ni ko'rsatish (noto'g'ri narx, boshqa foydalanuvchining ma'lumoti) bo'sh ekrandan ham xavfliroq.

Lekin biz xatoni cheklashni xohlaymiz: sharhlar ishlamasa — faqat sharhlar o'rnida xabar, sahifaning qolgan qismi ishlayversin. Buning uchun — Error Boundary.


Error Boundary nima

Error Boundary — bola komponentlaridagi render xatolarini ushlaydigan va ular o'rniga zaxira UI (fallback) ko'rsatadigan komponent. JavaScript dagi try/catch ga o'xshaydi, lekin komponentlar daraxti uchun.

<Header />
<MahsulotMalumoti />
<ErrorBoundary fallback={<p>Sharhlarni yuklab bo'lmadi.</p>}>
  <Sharhlar />                    {/* xato shu yerda qoladi */}
</ErrorBoundary>
<Footer />

Sharhlar ichida xato bo'lsa — faqat shu blok fallback ga almashadi.


Class komponent bilan

Error Boundary — React da hali ham faqat class komponent bilan yoziladigan yagona narsa (hook ekvivalenti yo'q):

import { Component } from 'react';

class ErrorBoundary extends Component {
  state = { xato: null };

  static getDerivedStateFromError(xato) {
    // Render paytida: fallback ko'rsatish uchun state ni yangilash
    return { xato };
  }

  componentDidCatch(xato, info) {
    // Xatoni qayd qilish — konsol yoki monitoring xizmati
    console.error(xato, info.componentStack);
  }

  render() {
    if (this.state.xato) {
      return this.props.fallback;
    }
    return this.props.children;
  }
}
Metod Vazifasi
static getDerivedStateFromError(xato) Xato bo'lganda yangi state qaytaradi → fallback
componentDidCatch(xato, info) Qo'shimcha amallar: loglash; info.componentStack — qaysi komponentda

Uni bir marta yozib, butun ilovada ishlatasiz.


react-error-boundary — tayyor yechim

Amalda ko'pincha tayyor kutubxona ishlatiladi:

npm install react-error-boundary
import { ErrorBoundary } from 'react-error-boundary';

function XatoBloki({ error, resetErrorBoundary }) {
  return (
    <div role="alert">
      <p>Nimadir noto'g'ri ketdi.</p>
      <pre style={{ color: 'red' }}>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Qayta urinish</button>
    </div>
  );
}

<ErrorBoundary
  FallbackComponent={XatoBloki}
  onError={(error, info) => logXizmati.yubor(error, info)}
  onReset={() => {
    // qayta urinishdan oldin holatni tozalash
  }}
>
  <Sharhlar />
</ErrorBoundary>
  • FallbackComponent — xato bo'lganda ko'rsatiladigan komponent; error va resetErrorBoundary ni oladi
  • resetErrorBoundary() — boundary ni tiklaydi va bolalarni qayta render qiladi
  • onError — loglash

resetKeys — avtomatik tiklash

const { id } = useParams();

<ErrorBoundary FallbackComponent={XatoBloki} resetKeys={[id]}>
  <MahsulotTafsiloti id={id} />
</ErrorBoundary>

resetKeys dagi qiymat o'zgarganda (foydalanuvchi boshqa mahsulotga o'tdi) boundary avtomatik tiklanadi. Busiz xato bloki boshqa sahifada ham qolib ketardi.

[!WARNING] Production da foydalanuvchiga error.message yoki stack trace ni ko'rsatmang — u tushunarsiz va ba'zan ichki ma'lumotni (fayl yo'llari, server tafsilotlari) oshkor qiladi. Foydalanuvchiga — oddiy, tushunarli xabar va harakat ("Qayta urinish", "Bosh sahifaga"); tafsilotlar — monitoring xizmatiga (Sentry va boshqalar).


Nimani ushlamaydi

Error Boundary faqat render paytidagi xatolarni ushlaydi:

Ushlaydi ✅ Ushlamaydi ❌
Komponent render qilayotganda Event handler lar (onClick ichida)
useEffect, useLayoutEffect ichida (sinxron) Async kod (setTimeout, fetch().then, await dan keyin)
Bola komponentlar konstruktorlari Server-side rendering
lazy komponent yuklanmasa (69-dars) Error Boundary ning o'zidagi xato

Event handler dagi xatolar

function SaqlashTugmasi() {
  const [xato, setXato] = useState(null);

  async function saqla() {
    try {
      await api.saqla();
    } catch (e) {
      setXato(e.message);          // oddiy try/catch + state
    }
  }

  return (
    <>
      <button onClick={saqla}>Saqlash</button>
      {xato && <p role="alert">{xato}</p>}
    </>
  );
}

Event handler da xato UI ni buzmaydi — React render paytida emas. Uni oddiy try/catch bilan ushlang.

Async xatoni boundary ga uzatish

Agar async xato boundary orqali ko'rsatilishi kerak bo'lsa:

import { useErrorBoundary } from 'react-error-boundary';

function Profil() {
  const { showBoundary } = useErrorBoundary();

  useEffect(() => {
    api.getProfil().then(setProfil).catch(showBoundary);   // eng yaqin boundary ga
  }, []);
}

TanStack Query bilan — throwOnError: true parametri xatoni render paytida tashlaydi va eng yaqin boundary ushlaydi.


Qayerga qo'yish kerak

// 1. Ilovaning eng yuqorisida — oxirgi himoya
<ErrorBoundary FallbackComponent={ToliqSahifaXatosi}>
  <App />
</ErrorBoundary>

// 2. Har bir sahifa (route) atrofida — bir sahifa buzilsa, navigatsiya ishlaydi
<Layout>
  <ErrorBoundary FallbackComponent={SahifaXatosi} resetKeys={[location.pathname]}>
    <Suspense fallback={<Skeleton />}>
      <Outlet />
    </Suspense>
  </ErrorBoundary>
</Layout>

// 3. Mustaqil vidjetlar atrofida — sharhlar, grafik, reklama, chat
<ErrorBoundary fallback={<p>Grafik mavjud emas</p>}>
  <Grafik />
</ErrorBoundary>

Qoida: boundary ni shunday joylashtiringki, xato bo'lganda iloji boricha ko'p UI ishlab qolsin, lekin fallback mantiqiy bo'lsin (har bir tugma atrofida boundary — ortiqcha).

[!TIP] React Router ning data router idan foydalansangiz (createBrowserRouter), har bir route uchun errorElement bor — u o'sha route dagi xatolar uchun o'rnatilgan Error Boundary vazifasini bajaradi.


Monitoring

Production dagi xatolar haqida foydalanuvchilar odatda yozmaydi — shunchaki ketib qoladi. Shuning uchun xatolarni avtomatik yig'ish kerak:

<ErrorBoundary
  FallbackComponent={XatoBloki}
  onError={(error, info) => {
    Sentry.captureException(error, {
      extra: { componentStack: info.componentStack },
    });
  }}
>

React 19 da createRoot ga global ishlov beruvchilar ham berish mumkin:

createRoot(document.getElementById('root'), {
  onCaughtError: (error, info) => { /* boundary ushlagan xatolar */ },
  onUncaughtError: (error, info) => { /* hech kim ushlamagan xatolar */ },
}).render(<App />);

Nima uchun shunday?

Nima uchun React render xatosida butun ilovani o'chiradi — buzilgan qismni ko'rsatib qo'ysa yaxshiroq emasmi?

Chunki "buzilgan qism" — oldindan aytib bo'lmaydigan holat. Render yarmida to'xtagan komponent noto'g'ri narxni, boshqa foydalanuvchining ma'lumotini yoki ishlamaydigan "To'lash" tugmasini ko'rsatishi mumkin. Foydalanuvchi uchun noto'g'ri ma'lumot — ma'lumot yo'qligidan xavfliroq: bo'sh ekranni ko'rgan odam sahifani yangilaydi; noto'g'ri summani ko'rgan odam esa unga ishonib, to'lov qilishi mumkin. Shuning uchun React xavfsiz tomonni tanlaydi — hammasini olib tashlaydi. Error Boundary esa sizga — ilova muallifiga — "bu qism mustaqil, uning xatosi qolganlarga ta'sir qilmaydi, uning o'rniga mana bu xabarni ko'rsat" deb aniq chegara chizish imkonini beradi.


Xulosa

Eslab qoling:

  • Render xatosi — standart holatda butun ilova o'chadi
  • Error Boundary — bolalardagi render xatolarini ushlab, fallback UI ko'rsatadi
  • Class: getDerivedStateFromError (fallback) + componentDidCatch (loglash)
  • Amalda: react-error-boundary — FallbackComponent, resetErrorBoundary, resetKeys, onError
  • Ushlamaydi: event handler lar, async kod — ular uchun try/catch; kerak bo'lsa showBoundary
  • Joylashuv: ilova ustida, har bir route atrofida, mustaqil vidjetlar atrofida
  • Foydalanuvchiga — tushunarli xabar; tafsilotlar — monitoring xizmatiga

Keyingi dars

80-dars: Portals — modal, tooltip uchun, DOM dan tashqariga chiqish