IlmHamroh
JavaScript Full-stack/18-qism. React chuqur8/42-dars24 daqiqa
Mundarija (34)

Error boundary: React'da xatoni ushlash va tiklash — oq ekran o'rniga "Qayta urinish"

Qisqacha: Render paytida komponent xato tashlasa, React butun ilovani o'chiradi — oq ekran. Error boundary (xato chegarasi) — ichidagi komponentlar xatosini ushlab, butun ilova o'rniga faqat o'z qismida zaxira UI (fallback) ko'rsatadigan komponent. U hozircha faqat class bo'la oladi: static getDerivedStateFromError xatodan keyingi state'ni qaytaradi, componentDidCatch esa xatoni log qiladi. Tiklash — state'ni tozalash ("Qayta urinish" tugmasi yoki kalit o'zgarganda). Chegara render, effekt, use va Action xatolarini ushlaydi, lekin hodisa ishlovchisi va setTimeout dagi xatoni ushlamaydi. Ushlangan va ushlanmagan xatolar createRoot ning onCaughtError / onUncaughtError sozlamalariga keladi.

Bu darsda

  • O'z error boundary'ingizni yozasiz va bitta buzuq komponent butun sahifani o'chirmasligini ko'rasiz.
  • "Qayta urinish" va kalit o'zgarganda avtomatik tiklashni (resetKeys) qo'shasiz.
  • Chegara nimani ushlaydi, nimani ushlamasligini jonli tekshirasiz — jumladan Action xatolarini.
  • react-error-boundary kutubxonasini va onCaughtError / onUncaughtError ni bilasiz.
  • Chegaralarni qayerga qo'yishni (granularity) tanlaysiz va vazifalar da ro'yxat hamda statistikani himoyalaysiz.

Oldin bilishingiz kerak: React ilovani debug qilish, Suspense, Actions, class sintaksisi.

1. Nega bu kerak?

React ilovani debug qilish darsida o'lchagan edik: vazifalar da bitta qator chizishda xato bo'lsa, #ilova ichida 0 element qoladi. Bitta buzuq vazifa — butun ilova yo'q. Forma, filtr, eksport — hammasi ishlayotgan edi, lekin foydalanuvchi ularni ko'rmaydi.

«Bahor» menyusida ham shunday bo'ldi. Bir taomning narxi serverdan null keldi, price.toFixed() xato tashladi va mehmon telefonida bo'sh oq sahifa ko'rdi. 40 ta taomdan bittasi buzuq edi — 39 tasi ham yo'qoldi.

Nega React bunday qattiq? Uning hujjati shunday tushuntiradi: buzuq UI'ni ko'rsatishdan ko'ra hech narsa ko'rsatmaslik xavfsizroq. Noto'g'ri summa ko'rsatgan to'lov sahifasi oq ekrandan yomonroq. Lekin "hamma narsa yoki hech narsa" — juda qo'pol. Bizga o'rtasi kerak: buzuq qism o'rnida xabar, qolgani ishlaydi.

Hayotiy o'xshatish: uydagi elektr shchiti. Har xona o'z avtomatiga ega. Oshxonada choynak qisqa tutashuv qilsa, faqat oshxonaning avtomati tushadi. Mehmonxonada chiroq yonib turadi. Avtomat yo'q uyda esa butun uy qorong'i bo'ladi. Error boundary — shu avtomat. Qaysi xonaga qo'yishni siz tanlaysiz.

2. Birinchi chegara

2.1 Class komponent — nega

Error boundary hozircha faqat class komponent bo'la oladi. Class komponent — funksiya o'rniga JavaScript klassi bilan yozilgan eski uslubdagi komponent. Uni Class komponentlarni o'qish darsida to'liq ko'ramiz. Bugun bitta tayyor qolip yetarli: chegara uchun kerak bo'lgan ikki metodning hook'dagi o'xshashi yo'q. React jamoasi buni hujjatda ochiq aytadi va react-error-boundary kutubxonasini tavsiya qiladi. Uni ham ko'ramiz.

Qolipni o'qishga yordam beradigan uch narsa:

Class'da Funksiyada o'xshashi
this.state = { … } yoki state = { … } maydoni useState boshlang'ich qiymati
this.setState({ … }) setX(…)
render() { return …; } komponent funksiyasining return i
this.props.children { children } prop'i

2.2 Buzuq taom

jsx
import { Component } from "react";

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

  // Render paytida: xatodan keyingi state
  static getDerivedStateFromError(error) {
    return { error };
  }

  // Commit'dan keyin: xatoni log qilish
  componentDidCatch(error, info) {
    console.log("log serverga:", error.message);
  }

  render() {
    if (this.state.error) {
      return <p role="alert">Bu taomni ko'rsatib bo'lmadi.</p>;
    }
    return this.props.children;
  }
}

function Dish({ name, price }) {
  return (
    <li>
      {name} — {price.toFixed(0)} so'm
    </li>
  );
}

export default function App() {
  return (
    <ul>
      <Dish name="Osh" price={35000} />
      <ErrorBoundary>
        <Dish name="Lag'mon" price={null} />
      </ErrorBoundary>
      <Dish name="Manti" price={30000} />
    </ul>
  );
}

Konsolda:

text
TypeError: Cannot read properties of null (reading 'toFixed')
The above error occurred in the <Dish> component.
React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.
log serverga: Cannot read properties of null (reading 'toFixed')

Ekranda:

text
Osh — 35000 so'm
Bu taomni ko'rsatib bo'lmadi.
Manti — 30000 so'm

Lag'mon o'rnida — xabar, osh va manti joyida. Chegarasiz bu misol "Komponentni chizishda xato" qutisi bilan tugardi.

Konsoldagi dastlabki uch qatorni React o'zi yozdi (dev'da). Tarjimasi: "Yuqoridagi xato <Dish> komponentida yuz berdi. React bu komponentlar daraxtini siz bergan ErrorBoundary chegarasi yordamida boshidan qayta yaratishga harakat qiladi." To'rtinchisi — bizning componentDidCatch.

2.3 Ikki metod — ikki bosqich

Nega ikkita metod? Ular React ishining ikki bosqichiga to'g'ri keladi (Pure komponentlar dagi render va commit):

Metod Qachon Nima qiladi Qoida
static getDerivedStateFromError(error) render bosqichida yangi state qaytaradi sof: faqat return, yon ta'sir yo'q
componentDidCatch(error, info) commit'dan keyin log, analitika yon ta'sir mumkin; info.componentStack bor

static — metod klassning o'ziga tegishli, nusxaga emas (static a'zolar). Shuning uchun uning ichida this yo'q: u faqat xatoni oladi va state qaytaradi. Render bosqichida React yon ta'sirni xohlamaydi — StrictMode uni ikki marta chaqirishi mumkin. Log esa commit'dan keyin, bir marta.

flowchart TD
  A["Dish render: throw"] --> B["React: eng yaqin chegara?"]
  B -- "topildi" --> C["getDerivedStateFromError → error"]
  C --> D["Chegara qayta render: fallback"]
  D --> E["Commit"]
  E --> F["componentDidCatch: log"]
  B -- "yo'q" --> G["Ildiz bo'shaydi, onUncaughtError"]

Diagrammadagi ikki yo'lga qarang. Chegara bor — xato qisman. Yo'q — butun ildiz bo'shaydi, xato onUncaughtError ga ketadi (React ilovani debug qilish).

Tekshirib ko'ring: getDerivedStateFromError ichida fetch("/log", …) bilan xatoni serverga yuborish yaxshi fikrmi?

Javob

Yo'q. Bu metod render bosqichida ishlaydi va sof bo'lishi kerak. React render'ni qayta urinishi yoki StrictMode'da ikki marta chaqirishi mumkin — log ikki marta ketadi. Yon ta'sir (log, analitika) — componentDidCatch da: u commit'dan keyin, bir marta.

3. Tiklash

3.1 "Qayta urinish"

Xato ko'pincha vaqtinchalik: server bir lahza noto'g'ri javob berdi, keyingi so'rov to'g'ri bo'ladi. Chegara state'ini tozalasak, React ichidagi komponentlarni qaytadan chizadi. Tugmali chegara:

jsx
import { Component } from "react";

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

  static getDerivedStateFromError(error) {
    return { error };
  }

  // arrow funksiya — this doim shu chegara
  reset = () => {
    this.setState({ error: null });
  };

  render() {
    if (this.state.error) {
      return (
        <div role="alert">
          <p>Narxlar yuklanmadi.</p>
          <button onClick={this.reset}>Qayta urinish</button>
        </div>
      );
    }
    return this.props.children;
  }
}

// "Server" 200 ms dan keyin tiklanadi
let serverUp = false;
setTimeout(() => {
  serverUp = true;
}, 200);

function Prices() {
  if (!serverUp) throw new Error("tarmoq uzildi");
  return <p>Osh — 35000 so'm</p>;
}

export default function App() {
  return (
    <ErrorBoundary>
      <Prices />
    </ErrorBoundary>
  );
}

Konsolda:

text
Error: tarmoq uzildi
The above error occurred in the <Prices> component.
React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.

Ekranda:

text
Osh — 35000 so'm

Ochilishda server "ishlamayapti" — ekran("Narxlar yuklanmadi.") fallback'ni tasdiqladi. 200 ms da server tiklandi, lekin ekranda hali xabar: chegara o'z-o'zidan qayta urinmaydi. «Qayta urinish» — reset state'ni tozaladi, React Prices ni yangidan chizdi va narx chiqdi.

Bilish foydali bir detal: React xatoni ko'rganda chegarani darhol qo'llamaydi. Avval butun daraxtni yana bir marta, sinxron chizib ko'radi. Ikkinchi urinish ham xato bersa — fallback. Muvaffaqiyatli bo'lsa, React o'zi tiklanadi va buni onRecoverableError ga yozadi (pastda, «onCaughtError va onUncaughtError» bo'limida).

reset — arrow funksiya maydoni. Oddiy metod bo'lganda onClick={this.reset} ichida this yo'qolardi (this yo'qolishi). Class komponentlarda bu eng ko'p uchraydigan tuzoq — Class komponentlarni o'qish da qaytamiz.

3.2 Kalit o'zgarganda — avtomatik

Ba'zan tugma kerak emas: ma'lumot o'zgarsa, xato ham o'tib ketgan bo'lishi mumkin. Masalan, mehmon boshqa taomni tanladi — eski taomning xatosini ko'rsatib turishning ma'nosi yo'q. Chegaraga tiklash kaliti beramiz. U o'zgarsa — chegara o'zi tozalanadi. Kutubxonalarda bu resetKeys deyiladi:

jsx
import { Component, useState } from "react";

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

  static getDerivedStateFromError(error) {
    return { error };
  }

  // Kalit o'zgardi va xato bor — tiklaymiz
  componentDidUpdate(prevProps) {
    if (
      this.state.error &&
      !Object.is(prevProps.resetKey, this.props.resetKey)
    ) {
      this.setState({ error: null });
    }
  }

  render() {
    if (this.state.error) {
      return <p role="alert">{this.state.error.message}</p>;
    }
    return this.props.children;
  }
}

const PRICES = { Osh: null, Manti: 30000 };

function DishPrice({ dish }) {
  const price = PRICES[dish];
  if (price === null) throw new Error(`${dish}: narx yo'q`);
  return <p>{dish} — {price} so'm</p>;
}

export default function App() {
  const [dish, setDish] = useState("Osh");
  return (
    <div>
      <button onClick={() => setDish("Osh")}>Osh</button>{" "}
      <button onClick={() => setDish("Manti")}>Manti</button>
      <ErrorBoundary resetKey={dish}>
        <DishPrice dish={dish} />
      </ErrorBoundary>
    </div>
  );
}

Konsolda:

text
Error: Osh: narx yo'q
The above error occurred in the <DishPrice> component.
React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.

Ekranda:

text
Osh Manti
Manti — 30000 so'm

Birinchi ochilishda osh xato berdi — chegara ichida xabar. «Manti» bosildi, resetKey o'zgardi va chegara o'zi tiklandi. Object.is — === ning eng aniq shakli (NaN ni o'ziga teng deb biladi): React ham bog'liqliklarni shu bilan solishtiradi (Dependency massivi). vazifalar kanonidagi tiklashKaliti — aynan shu naqsh.

Muqobil yo'l ham bor — key (State'ning saqlanishi va key): <ErrorBoundary key={dish}>. Kalit o'zgarsa, React chegarani butunlay yangidan yaratadi. Ishlaydi, lekin ichidagi hamma state ham yo'qoladi. resetKey esa faqat xato bo'lganda tiklaydi.

4. Chegara nimani ushlaydi

4.1 Ushlamaydigan joylar

Chegara faqat React'ning o'zi chaqirgan kodni ushlaydi: render, effektlar, ref callback'lar, use, Action'lar. Siz o'zingiz chaqirgan kod — hodisa ishlovchisi, taymer — chegaradan o'tmaydi:

jsx
import { Component } from "react";

class ErrorBoundary extends Component {
  state = { error: null };
  static getDerivedStateFromError(error) {
    return { error };
  }
  render() {
    if (this.state.error) return <p role="alert">Chegara ushladi</p>;
    return this.props.children;
  }
}

function OrderButton() {
  function handleClick() {
    throw new Error("Hodisada xato"); // ❌ chegara ko'rmaydi
  }
  return <button onClick={handleClick}>Buyurtma</button>;
}

export default function App() {
  return (
    <ErrorBoundary>
      <OrderButton />
    </ErrorBoundary>
  );
}

Konsolda:

text
Uncaught Error: Hodisada xato
Error: Hodisada xato

Ekranda "Chegara ushladi" yo'q — tugma joyida. Xato window ning error hodisasiga ketdi (Ushlanmagan xatolar). Sababi oddiy: bosish paytida React render qilmayapti. Hodisa ishlovchisi — oddiy JavaScript funksiyasi, uni brauzer chaqiradi. Ilova yiqilmadi ham: render buzilmagan, ekran to'g'ri.

Xato qayerda Chegara ushlaydimi Nima qilish kerak
render (komponent tanasi) ha —
useEffect, useLayoutEffect, ref callback ha —
use(promise) — rad etilgan Promise ha —
<form action>, useTransition dagi Action ha —
lazy komponent yuklanmadi ha keyingi dars
hodisa ishlovchisi (onClick) yo'q try/catch + state
setTimeout, fetch().then yo'q try/catch yoki chegaraga yuborish
hook'siz startTransition dagi Action yo'q Action ichida ushlash
chegaraning o'zidagi xato yo'q yuqoridagi chegara
serverda render (SSR) yo'q framework vositalari

4.2 Action xatosi — ushlanadi

Actions darsida Action'dagi throw ilovani o'chirgan edi — chegara yo'q edi. Endi chegara bilan:

jsx
import { Component } from "react";

class ErrorBoundary extends Component {
  state = { error: null };
  static getDerivedStateFromError(error) {
    return { error };
  }
  render() {
    if (this.state.error) {
      const { message } = this.state.error;
      return <p role="alert">Bron saqlanmadi: {message}</p>;
    }
    return this.props.children;
  }
}

async function book() {
  await new Promise((resolve) => setTimeout(resolve, 200));
  throw new Error("500: server xatosi");
}

export default function App() {
  return (
    <ErrorBoundary>
      <form action={book}>
        <button>Bron qilish</button>
      </form>
    </ErrorBoundary>
  );
}

Konsolda:

text
Error: 500: server xatosi
The above error occurred in the <form> component.
React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.

Ekranda:

text
Bron saqlanmadi: 500: server xatosi

Forma Action'i <form> ga bog'langan — React qaysi chegaraga berishni biladi. Konsol ham shuni aytadi: "xato <form> komponentida". useTransition bergan startTransition ham komponentga bog'langan — u ham ushlanadi. Hook'siz startTransition esa hech qaysi komponentga tegishli emas — jadvaldagi "yo'q" qatori.

4.3 Hodisa xatosini chegaraga yuborish

Ba'zan hodisadagi xatoni ham chegara ko'rsatishi kerak: masalan, "Yangilash" tugmasi bosilganda server yiqildi. Buning uchun xatoni render ichiga olib kirish kerak. Mashhur usul — state updater'ida throw:

jsx
import { Component, useState } from "react";

class ErrorBoundary extends Component {
  state = { error: null };
  static getDerivedStateFromError(error) {
    return { error };
  }
  render() {
    if (this.state.error) {
      return <p role="alert">Chegara: {this.state.error.message}</p>;
    }
    return this.props.children;
  }
}

// Xatoni eng yaqin chegaraga yuboradigan hook
function useShowBoundary() {
  const [, setState] = useState(null);
  return (error) => {
    setState(() => {
      throw error; // updater render paytida chaqiriladi
    });
  };
}

function RefreshButton() {
  const showBoundary = useShowBoundary();
  function handleClick() {
    setTimeout(() => {
      showBoundary(new Error("server javob bermadi"));
    }, 50);
  }
  return <button onClick={handleClick}>Yangilash</button>;
}

export default function App() {
  return (
    <ErrorBoundary>
      <RefreshButton />
    </ErrorBoundary>
  );
}

Konsolda:

text
Error: server javob bermadi
The above error occurred in the <RefreshButton> component.
React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.

Ekranda:

text
Chegara: server javob bermadi

Updater funksiya ni React keyingi render paytida chaqiradi. Uning ichidagi throw — render xatosi. Endi chegara uni ko'radi. react-error-boundary dagi useErrorBoundary().showBoundary ichida aynan shu g'oya bor.

Tekshirib ko'ring: OrderButton ning handleClick ida xatoni try/catch bilan ushlab, catch da throw err qilsak, chegara uni ushlaydimi?

Javob

Yo'q. Qayta tashlangan xato baribir hodisa ishlovchisidan chiqadi — render emas. Chegaraga yetkazish uchun uni render ichiga olib kirish kerak: «Hodisa xatosini chegaraga yuborish» bo'limidagi showBoundary yoki state'ga yozib, render'da throw.

5. react-error-boundary kutubxonasi

Har loyihada o'z class'ini yozish o'rniga, ko'pchilik react-error-boundary kutubxonasini ishlatadi (2026-10-13 holatiga npm'dagi oxirgi versiya — 6.1.6; React 18 va 19 bilan ishlaydi). U funksiya komponentlar bilan ishlaydigan qulay API beradi. Saytdagi «Ishga tushir» begona paketni yuklamaydi — bu kodni o'z loyihangizda sinang (npm install react-error-boundary):

jsx
import {
  ErrorBoundary,
  useErrorBoundary,
} from "react-error-boundary";

// Fallback — oddiy funksiya komponent
function MenuError({ error, resetErrorBoundary }) {
  return (
    <div role="alert">
      <p>Menyu yuklanmadi: {error.message}</p>
      <button onClick={resetErrorBoundary}>Qayta urinish</button>
    </div>
  );
}

function RefreshButton() {
  const { showBoundary } = useErrorBoundary();
  async function handleClick() {
    try {
      await refreshMenu();
    } catch (error) {
      showBoundary(error); // hodisadagi xato — chegaraga
    }
  }
  return <button onClick={handleClick}>Yangilash</button>;
}

export default function MenuPage({ category }) {
  return (
    <ErrorBoundary
      FallbackComponent={MenuError}
      resetKeys={[category]} // kategoriya o'zgarsa — tiklanadi
      onReset={() => clearMenuCache()} // tiklashdan oldin
      onError={(error, info) => logToServer(error, info)}
    >
      <Menu category={category} />
      <RefreshButton />
    </ErrorBoundary>
  );
}

Bugun qo'lda yozgan hamma narsaning nomi bor:

Kutubxonada Darsda qo'lda
FallbackComponent (error, resetErrorBoundary) render dagi fallback + reset
resetKeys={[…]} resetKey + componentDidUpdate
onError componentDidCatch
useErrorBoundary().showBoundary useShowBoundary
onReset reset dan oldingi tozalash

Kutubxona ichida — o'sha class. Shuning uchun class'ni bir marta o'z qo'lingiz bilan yozganingiz foydali: kutubxona sehr emas.

6. onCaughtError va onUncaughtError

6.1 Ildiz darajasidagi "qora quti"

Chegara xatoni foydalanuvchidan yashiradi. Lekin dasturchi undan xabar topishi kerak — aks holda xato oylab sezilmaydi. React ilovani debug qilish darsida onUncaughtError ni yozgan edik. React 19 da uning juftlari bor:

jsx
import { Component } from "react";
import { createRoot } from "react-dom/client";

class ErrorBoundary extends Component {
  state = { error: null };
  static getDerivedStateFromError(error) {
    return { error };
  }
  render() {
    if (this.state.error) return <p role="alert">Xato ushlandi</p>;
    return this.props.children;
  }
}

function Price() {
  throw new Error("narx yo'q");
}

const root = createRoot(document.getElementById("root"), {
  onCaughtError(error, info) {
    // component stack'ning birinchi qatori: "at Price (manzil)"
    const first = info.componentStack.trim().split("\n")[0];
    const name = first.split(" (")[0];
    console.log("ushlandi:", error.message, "|", name);
  },
  onUncaughtError(error) {
    console.log("ilova yiqildi:", error.message);
  },
});

root.render(
  <ErrorBoundary>
    <Price />
  </ErrorBoundary>,
);

Konsolda:

text
ushlandi: narx yo'q | at Price

Ekranda:

text
Xato ushlandi

onCaughtError berilgach, React'ning standart uch qatorli xabari yo'qoldi — endi log bizning qo'limizda. info.componentStack — xato qaysi komponentlar ichida bo'lgani. Uning birinchi qatori at Price (…): qavs ichida fayl manzili va satr raqami. Ular joyga qarab o'zgaradi, shuning uchun biz faqat komponent nomini chiqardik.

Sozlama Qachon chaqiriladi
onCaughtError chegara xatoni ushladi (ilova ishlayapti)
onUncaughtError chegara yo'q, ildiz bo'shadi
onRecoverableError React o'zi tiklandi (masalan, hydration nomuvofiqligi)

Real loyihada uchalasi xatolarni kuzatish xizmatiga (Sentry va boshqalar) yuboradi — Xatolarni kuzatish: Sentry. Dev'da konsol, production'da — server.

7. Chegaralarni qayerga qo'yish

7.1 Granularity — mayda yoki yirik

Har komponent atrofida chegara — ortiqcha: kod ko'payadi, sahifa "teshik-teshik" bo'ladi. Bitta chegara ildizda — oq ekrandan biroz yaxshi, xolos. To'g'ri javob — foydalanuvchi uchun mustaqil qismlar atrofida:

flowchart TD
  R["Ildiz chegarasi: 'Nimadir buzildi, sahifani yangilang'"] --> H["Sarlavha, menyu"]
  R --> P["Sahifa chegarasi"]
  P --> L["Taomlar ro'yxati chegarasi"]
  P --> S["Sharhlar chegarasi"]
  P --> F["Buyurtma formasi"]
  R --> T["Savat chegarasi"]

Diagrammada uch daraja. Ildizdagi — oxirgi himoya, "sahifani yangilang". Sahifa darajasidagi — sahifa buzilsa ham menyu va savat ishlaydi. Ichkaridagilar — sharhlar buzilsa ham, taomlar ro'yxati va forma ishlaydi.

Qaror savollari:

  • Bu qism o'zi alohida foydalimi? Sharhlarsiz menyu foydali — sharhlar alohida chegarada. Narxsiz buyurtma formasi foydasiz — ular bitta chegarada.
  • Fallback qanday ko'rinadi? Kichik vidjet uchun — bir qator matn. Sahifa uchun — "Qayta urinish" va bosh sahifaga havola.
  • Tiklash qanday? Ma'lumot o'zgarsa — resetKeys. Kod yuklanmasa — sahifani yangilash (keyingi dars).

7.2 Suspense bilan juftlik

Suspense "kutilmoqda" ni, chegara — "xato" ni ko'rsatadi. Ular ko'pincha birga turadi:

jsx
<ErrorBoundary fallback={<p>Sharhlar yuklanmadi.</p>}>
  <Suspense fallback={<p>Sharhlar yuklanmoqda…</p>}>
    <Reviews />
  </Suspense>
</ErrorBoundary>

Tartib muhim: chegara tashqarida. Shunda Reviews ning Promise'i rad etilsa ham, kutish paytida ham shu qism o'z joyida qoladi.

8. Vazifalar qadami: ro'yxat va statistika atrofida chegara (holat 08)

8.1 XatoChegarasi.tsx

Yangi fayl, kanondan aynan; ba'zi izohlar telefon uchun qatorlarga qayta bo'lingan (TypeScript turlarini React + TS darslarida chuqur ko'ramiz):

tsx
// XatoChegarasi.tsx — error boundary (18/#08). Render paytidagi
// xato (komponent throw qildi) butun ilovani oq ekranga
// aylantirmaydi (17/#45 da shunday edi: #ilova ichida 0 element):
// faqat shu chegara ichi fallback'ga almashadi, qolgan ilova
// ishlayveradi.
// Hozircha faqat CLASS komponent bo'la oladi: hooklarda
// getDerivedStateFromError/componentDidCatch o'xshashi yo'q (R-59 —
// class komponentni o'qish shu yerda kerak bo'ladi).
// Ushlamaydi: hodisa ishlovchisidagi xato (onClick), async kod
// (setTimeout, await), chegaraning o'zidagi xato — ular uchun try/catch
import { Component } from "react";
import type { ErrorInfo, ReactNode } from "react";

type Props = {
  children: ReactNode;
  // Xato bo'lganda nima ko'rinadi; qaytaUrin — chegarani tiklaydi
  fallback: (xato: unknown, qaytaUrin: () => void) => ReactNode;
  // Bu qiymat o'zgarsa — chegara o'zi tiklanadi (masalan, ro'yxat
  // serverdan yangilandi — ehtimol xato ma'lumot ham tuzaldi)
  tiklashKaliti?: unknown;
  onXato?: (xato: unknown, malumot: ErrorInfo) => void;
};

type Holat = { readonly xato: { readonly qiymat: unknown } | null };

export class XatoChegarasi extends Component<Props, Holat> {
  override state: Holat = { xato: null };

  // Render bosqichi: xatodan keyingi holat (sof — faqat state
  // qaytaradi). { qiymat } o'rami: throw null/undefined bo'lsa ham
  // "xato bor" bilinsin
  static getDerivedStateFromError(xato: unknown): Holat {
    return { xato: { qiymat: xato } };
  }

  // Commit bosqichi: yon ta'sir (log) — componentStack bilan
  override componentDidCatch(xato: unknown, malumot: ErrorInfo): void {
    this.props.onXato?.(xato, malumot);
  }

  override componentDidUpdate(oldingi: Props): void {
    if (
      this.state.xato !== null &&
      !Object.is(oldingi.tiklashKaliti, this.props.tiklashKaliti)
    ) {
      this.qaytaUrin();
    }
  }

  // Arrow — this doim shu nusxa (fallback'dagi tugmaga uzatiladi)
  qaytaUrin = (): void => {
    this.setState({ xato: null });
  };

  override render(): ReactNode {
    if (this.state.xato !== null) {
      return this.props.fallback(
        this.state.xato.qiymat,
        this.qaytaUrin,
      );
    }
    return this.props.children;
  }
}

Darsdagi hamma narsa bor: ikki bosqich, tiklashKaliti (resetKeys), arrow qaytaUrin. Ikki yangi detal:

  • { qiymat } o'rami. JavaScript'da throw null ham mumkin. state.xato = null bo'lsa, "xato yo'q" bilan "null tashlandi" farqlanmay qolardi. O'ram bu ikkisini ajratadi.
  • fallback — funksiya. Har joy o'z fallback'ini beradi va unga qaytaUrin keladi. Bu — render prop naqshi (HOC va render props).

8.2 Qayerga qo'yildi

Ikki joy — ikki mustaqil qism. Ro'yxat (App.tsx):

tsx
      <XatoChegarasi
        tiklashKaliti={vazifalar}
        fallback={(_xato, qaytaUrin) => (
          <div id="royxat-xato" role="alert">
            <p>Ro'yxatni ko'rsatishda xato yuz berdi.</p>
            <button type="button" onClick={qaytaUrin}>
              Qayta urinish
            </button>
          </div>
        )}
      >
        <ul ref={royxatRef} id="royxat" …>…</ul>
      </XatoChegarasi>

tiklashKaliti={vazifalar} — ro'yxat serverdan yoki boshqa tabdan yangilansa, chegara o'zi qayta urinadi. Forma, statistika, eksport chegaradan tashqarida — ro'yxat buzilsa ham ishlaydi.

Statistika paneli (StatistikaPaneli.tsx): chegara <Suspense> ning tashqarisida, «Suspense bilan juftlik» bo'limidagidek. use() dagi rad etilgan hisob Promise'i shu yerda ushlanadi. statistika-vadasi.ts ga bitta qator qo'shildi — rad etilgan Promise keshdan o'chadi:

ts
    // Rad etilgan Promise keshda qolmasin: «Qayta urinish» (18/#08)
    // yangisini so'raydi, eskisini emas
    vada.catch(() => kesh.delete(vazifalar));

Usiz «Qayta urinish» keshdan o'sha rad etilgan Promise'ni olardi — va yana xato. Bu — use() darsidagi "Xato ham keshlanadi" ogohlantirishi.

8.3 main.tsx — onCaughtError

tsx
  onCaughtError(xato, malumot) {
    console.error(
      `Xato ushlandi (chegara): ${xatoMatni(xato)}`,
      malumot.componentStack ?? "",
    );
  },

Kanon izohi: "error boundary USHLAGAN xato ham shu yerga keladi — ilova ishlayveradi, lekin xato yashirinib qolmasin". 28-qismda bu yer serverga log yuboradi.

8.4 Haqiqiy o'lchov

Kanonni qurishda vaqtinchalik nusxa yasaldi: ?xato=qator bo'lsa, birinchi vazifa qatori throw qiladi (olchov/xato-chegarasi.mjs, Chrome 155). Natija (natijalar/08-xato-chegarasi.txt):

text
{"ilovaElementlari":69,"royxatXato":"Ro'yxatni ko'rsatishda xato yuz berdi.Qayta urinish","royxatBor":false,"hisob":"2 ta vazifa qoldi, 1 ta bajarildi"}
qo'shishdan keyin: {"ilovaElementlari":69, …, "hisob":"3 ta vazifa qoldi, 1 ta bajarildi"}

#ilova ichida 69 element — v7 da 0 edi. Ro'yxat o'rnida xabar, lekin forma ishladi: vazifa qo'shildi va hisob 2 dan 3 ga o'zgardi. Konsolda — Xato ushlandi (chegara): Sinov: qator chizilmadi va component stack.

Bitta qator xato berganda #ilova ichidagi elementlar
  • v7 (chegarasiz)oq ekran0 ta
  • v8 (XatoChegarasi)faqat ro'yxat o'rnida xabar69 ta

Manba: vazifalar kanoni: 17/#45 o'lchovi va natijalar/08-xato-chegarasi.txt (Chrome 155)

8.5 Testlar va commit

tekshiruv/XatoChegarasi.test.tsx — uch test: ushlash va «Qayta urinish», tiklashKaliti, va hodisa ishlovchisidagi xatoni ushlamasligi (xato window ning error hodisasiga ketadi, fallback yo'q). Uchinchi test — «Ushlamaydigan joylar» jadvalining avtomatik isboti.

bash
git switch -c feat/xato-chegarasi
git add -A
git commit

Commit (kanon): feat: ro'yxat va statistika atrofida error boundary, onCaughtError. Tekshiruvlar (haqiqiy, tekshiruv-08.txt): node:test ℹ tests 163, Vitest Tests 36 passed (36) (+3), lint, format, tip — toza. Brauzer — 159/159 (uch marta), e2e-18 — 22/22. Diff: 6 fayl, +229 −29.

Bu qadamda e2e ssenariyda bitta o'zgarish topildi: validatsiya xatosi endi Action natijasi bo'lgani uchun keyingi renderda chiqadi. Ssenariy xato matni paydo bo'lishini kutadigan bo'ldi. Bu — oldingi darsdagi Action'larning natijasi.

9. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
bitta komponent xatosi — oq ekran chegara yo'q mustaqil qismlar atrofida chegara
onClick dagi xatoni chegara ko'rmaydi hodisa — render emas try/catch + state yoki showBoundary
«Qayta urinish» — yana o'sha xato rad etilgan Promise keshda qolgan rad etilsa keshdan o'chiring
this.reset ichida this — undefined oddiy metod, onClick ga uzatilgan arrow funksiya maydoni
log ikki marta ketadi yon ta'sir getDerivedStateFromError da log — componentDidCatch yoki onCaughtError da
chegara xatosi ham oq ekran xato fallback'ning o'zida fallback'ni sodda qiling, yuqorida yana bir chegara
boshqa taom tanlansa ham xato qoldi tiklash kaliti yo'q resetKeys / tiklashKaliti

10. Mashqlar

Mashqlar kurs/mashqlar/18/08-error-boundary/ da. Har yechimni «Ishga tushir» bilan sinang.

1-mashq (oson): ushlaydimi?

Har holat uchun ha yoki yo'q:

  • Komponent tanasida JSON.parse("{") xato tashladi.
  • onClick ichida price.toFixed() xato tashladi.
  • useEffect ichida throw new Error().
  • setTimeout(() => { throw new Error(); }, 100) — effekt ichida.
  • <form action> dagi async Action throw qildi.
Yechim
  • ha — render paytidagi xato.
  • yo'q — hodisa ishlovchisi; xato window ning error hodisasiga ketadi.
  • ha — effektni React o'zi chaqiradi.
  • yo'q — effekt tugagan, taymer keyinroq brauzer tomonidan chaqiriladi.
  • ha — forma Action'i <form> ga bog'langan, React xatoni chegaraga beradi.

2-mashq (o'rta): sharhlar buzilsa ham menyu ishlasin

Sahifada menyu va sharhlar bor. Reviews xato tashlaydi va butun sahifa yo'qoladi. ErrorBoundary class'ini yozing va faqat Reviews ni o'rang: sharhlar o'rnida role="alert" li "Sharhlar vaqtincha mavjud emas." chiqsin, menyu ko'rinib tursin.

jsx
function Menu() {
  return <p>Menyu: osh, lag'mon, manti</p>;
}

function Reviews() {
  throw new Error("sharhlar serveri yiqildi");
}

export default function App() {
  return (
    <div>
      <Menu />
      <Reviews />
    </div>
  );
}

Konsolda:

text
Uncaught Error: sharhlar serveri yiqildi
Yechim
jsx
import { Component } from "react";

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

  static getDerivedStateFromError(error) {
    return { error };
  }

  render() {
    if (this.state.error) {
      return <p role="alert">{this.props.fallbackText}</p>;
    }
    return this.props.children;
  }
}

function Menu() {
  return <p>Menyu: osh, lag'mon, manti</p>;
}

function Reviews() {
  throw new Error("sharhlar serveri yiqildi");
}

export default function App() {
  return (
    <div>
      <Menu />
      <ErrorBoundary fallbackText="Sharhlar vaqtincha mavjud emas.">
        <Reviews />
      </ErrorBoundary>
    </div>
  );
}

Konsolda:

text
Error: sharhlar serveri yiqildi
The above error occurred in the <Reviews> component.
React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.

Ekranda:

text
Menyu: osh, lag'mon, manti
Sharhlar vaqtincha mavjud emas.

Chegara faqat Reviews atrofida — Menu uning tashqarisida. Fallback matni prop orqali: bitta class'ni turli joylarda ishlatish uchun.

3-mashq (qiyin): stol tanlansa — tiklansin, «Qayta urinish» ham bo'lsin

Ikki tugma: «1-stol» va «2-stol». TableBill 1-stol uchun xato tashlaydi ("1-stol: hisob topilmadi"), 2-stol uchun "2-stol: 120000 so'm" chiqaradi. Chegara:

  1. Fallback'da xato matni (role="alert") va «Qayta urinish» tugmasi.
  2. Stol o'zgarsa — chegara o'zi tiklansin (resetKey).

Ishora: «"Qayta urinish"» va «Kalit o'zgarganda — avtomatik» bo'limlari; reset — arrow funksiya maydoni.

Yechim
jsx
import { Component, useState } from "react";

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

  static getDerivedStateFromError(error) {
    return { error };
  }

  componentDidUpdate(prevProps) {
    if (
      this.state.error &&
      !Object.is(prevProps.resetKey, this.props.resetKey)
    ) {
      this.reset();
    }
  }

  reset = () => {
    this.setState({ error: null });
  };

  render() {
    if (this.state.error) {
      return (
        <div role="alert">
          <p>{this.state.error.message}</p>
          <button onClick={this.reset}>Qayta urinish</button>
        </div>
      );
    }
    return this.props.children;
  }
}

const BILLS = { 2: 120000 };

function TableBill({ table }) {
  const bill = BILLS[table];
  if (bill === undefined) {
    throw new Error(`${table}-stol: hisob topilmadi`);
  }
  return <p>{table}-stol: {bill} so'm</p>;
}

export default function App() {
  const [table, setTable] = useState(1);
  return (
    <div>
      <button onClick={() => setTable(1)}>1-stol</button>{" "}
      <button onClick={() => setTable(2)}>2-stol</button>
      <ErrorBoundary resetKey={table}>
        <TableBill table={table} />
      </ErrorBoundary>
    </div>
  );
}

Konsolda:

text
Error: 1-stol: hisob topilmadi
The above error occurred in the <TableBill> component.
React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.

Ekranda:

text
1-stol 2-stol
2-stol: 120000 so'm

Ochilishda 1-stol xato berdi — fallback. «2-stol» bosildi: resetKey 1 → 2, componentDidUpdate chegarani tikladi va 2-stol hisobi chiqdi. «Qayta urinish» 1-stolda yana xato beradi — ma'lumot o'zgarmagan, bu to'g'ri xulq. reset ikki joyda ishlatiladi: tugmada va componentDidUpdate da.

4-mashq: Vazifalar qadami

feat/xato-chegarasi branch'ida XatoChegarasi.tsx ni yarating, ro'yxat va statistika panelini o'rang, main.tsx ga onCaughtError qo'shing, statistika-vadasi.ts da rad etilgan Promise'ni keshdan o'chiring. Uch Vitest testini yozing. Commit qiling va PR oching.

Yechim

Tekshiruv ro'yxati:

  • npm test — node 163, Vitest 36. lint, format:check, tip — toza.
  • Qo'lda sinov (keyin qaytaring): OddiyQator boshiga if (vazifa.id === 1) throw new Error("Sinov") yozing va npm run dev ni oching. Ro'yxat o'rnida xabar, forma ishlaydi, konsolda Xato ushlandi (chegara): Sinov va component stack.
  • Testdagi "hodisa ishlovchisidagi xatoni ushlamaydi" — window error hodisasini tinglab, preventDefault bilan jim qiladi.
  • Commit — feat: ro'yxat va statistika atrofida error boundary, onCaughtError. PR'ni birlashtiring.

11. Real ishda

  • Har jiddiy React ilovada ildizda bitta "Nimadir buzildi" chegarasi va muhim qismlar atrofida kichikroqlari bor. Kod ko'rigida "bu vidjet yiqilsa, sahifa ham yiqiladimi?" degan savol beriladi.
  • Next.js — har marshrut papkasidagi error.tsx fayli — avtomatik error boundary (Maxsus fayllar).
  • Sentry — Sentry.ErrorBoundary komponenti va onCaughtError / onUncaughtError bilan React 19 integratsiyasi (Xatolarni kuzatish: Sentry).
  • Intervyu savollari: "Error boundary nima va nega u class?", "nimani ushlamaydi?", "getDerivedStateFromError va componentDidCatch farqi?", "chegaralarni qayerga qo'yasiz?".

Xulosa

  • Render xatosi chegarasiz butun ilovani o'chiradi; error boundary uni bir qism bilan cheklaydi.
  • Chegara — class: getDerivedStateFromError (render, sof) + componentDidCatch (commit, log).
  • Tiklash — setState({ error: null }): tugma bilan yoki kalit o'zgarganda (resetKeys).
  • Ushlaydi: render, effekt, use, forma va useTransition Action'lari. Ushlamaydi: hodisa, taymer, hook'siz startTransition, o'zi.
  • react-error-boundary — FallbackComponent, resetKeys, showBoundary; onCaughtError/onUncaughtError — log uchun.
  • vazifalar: XatoChegarasi ro'yxat va statistika atrofida — bitta xatoda 0 o'rniga 69 element.

Keyingi dars: lazy va code splitting — og'ir komponentni kerak bo'lganda yuklash va chunk yuklanmasa nima qilish.

Manbalar

  • React: Component — "Catching rendering errors with an Error Boundary" — react.dev/reference/react/Component (2026-10 holati, React 19.3)
  • React: createRoot — onCaughtError, onUncaughtError, onRecoverableError — react.dev/reference/react-dom/client/createRoot
  • react-error-boundary — github.com/bvaughn/react-error-boundary
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Error boundary: React'da xatoni ushlash va tiklash — oq ekran o'rniga "Qayta urinish" — IlmHamroh