WisarWisar
Dasturlash kitobi/11-QISM — React38 daqiqa

11.12-bob: Error boundaries, portals, refs (ilg'or)

11-QISM — Frontend: React · 12-mavzu


1. Kirish va motivatsiya

Shu paytgacha React'ning asosiy oqimini — komponent, state, hooklar, routing, forma, performance — chuqur o'rgandik. Endi uchta ilg'or imkoniyatni ko'ramiz, ular oddiy ilovada doim ko'rinmaydi, lekin professional, ishonchli, qayta ishlatiladigan komponentlar (modal tizimi, UI kutubxonalar, murakkab interfeyslar) qurish uchun zarur: Error boundaries (xatolarni ushlab, butun ilova qulashining oldini olish), Portals (komponentni DOM daraxtining boshqa joyiga "teleport" qilish — modal, tooltip, dropdown), va refs (forwardRef, useImperativeHandle — DOM va komponentga imperativ kirish).

Bu uchchovi turli muammolarni hal qiladi, lekin bir umumiy mavzuga ega: ular React'ning odatdagi deklarativ oqimidan tashqari ishlaydigan holatlarni boshqaradi. Error boundary — xato yuz berganda (deklarativ render buzilganda) ilovani qutqaradi. Portal — komponent JSX'da bir joyda tursa ham, DOM'da boshqa joyga chiqishini ta'minlaydi (modal z-index/overflow muammolarini hal qiladi). Refs — React deklarativ qila olmaydigan imperativ amallarni (fokus, scroll, media play, child komponentni boshqarish) imkon beradi. Bularsiz siz oddiy ilova yoza olasiz, lekin kutubxona darajasidagi komponentlar yoza olmaysiz — shuning uchun bular senior React dasturchisining asbobi.

Bu bob: nega bu ilg'or vositalar (qanday muammolar), Error boundary (nima, qanday yoziladi — class getDerivedStateFromError/componentDidCatch, qayerga joylashtirish, nimani ushlamaydi — event handler/async, react-error-boundary kutubxonasi), Portals (nima — DOM teleport, createPortal, modal, event bubbling — React tree vs DOM tree), refs (useRef takror, forwardRef — ref uzatish, React 19 ref as prop, useImperativeHandle — imperativ API ochish, callback ref), va bularni birlashtirib professional modal/dropdown qurish. Har mavzuni to'liq va amaliy holatda ko'ramiz.

O'xshatish: Bu uch vosita — binoning maxsus tizimlari. Error boundary — bu yong'inga qarshi to'siq devorlari (firewall): binoning bir xonasida yong'in chiqsa (komponent xatosi), to'siq devor uni o'sha xonada ushlab qoladi — butun bino yonib ketmaydi (ilova qulamaydi), faqat o'sha xonada "ta'mirlanmoqda" belgisi (fallback) ko'rinadi. Portal — bu lift shaxtasi: lift kabinasi (modal) qaysi qavatda tugma bosilsa ham (JSX joyi), u shaxta orqali eng tepaga (DOM'ning tepasiga — body) chiqadi — devorlar (overflow: hidden, z-index) unga to'sqinlik qila olmaydi. Ref — bu maxsus xizmat kaliti: oddiy mehmon (deklarativ React) xonaga faqat eshikdan kiradi, lekin ba'zan texnik (siz) bevosita jihozga tegishi kerak (DOM elementni fokuslash, media'ni ishga tushirish) — ref shu imperativ kalit.

Nega muhim?

  • Ishonchli ilova — error boundary'siz bitta komponent xatosi butun ilovani oq ekranga aylantiradi (yomon).
  • Modal/overlay — har real ilovada modal, tooltip, dropdown bor — portal'siz ular overflow/z-index'da buziladi.
  • Kutubxona komponentlari — qayta ishlatiladigan UI (custom input, modal tizimi) forwardRef/useImperativeHandle talab qiladi.
  • Senior daraja — bu vositalar — murakkab, professional komponent dizaynining belgisi.

2. Nazariya — chuqur tushuntirish

2.1. Nega ilg'or vositalar — qanday muammolar

text
  3 MUAMMO, 3 VOSITA:

  MUAMMO 1 — bitta komponent XATOSI butun ilovani buzadi:
  // bir komponentda xato (undefined.map, null.name)  React BUTUN daraxtni o'chiradi  OQ EKRAN
   YECHIM: Error boundary (xatoni USHLA, faqat o'sha qismni zaxira UI bilan almashtir)

  MUAMMO 2 — modal/tooltip DOM ierarxiyasida "qamalgan":
  // <div style="overflow:hidden"> ichidagi modal — kesiladi; z-index ota bilan cheklangan
   YECHIM: Portal (modal'ni DOM'ning tepasiga — body'ga — chiqar, JSX joyidan qat'i nazar)

  MUAMMO 3 — child DOM/komponentga bevosita kirish kerak (imperativ):
  // fokus, scroll, media play, child input'ni tozalash — deklarativ qila olmaydi
   YECHIM: refs (forwardRef, useImperativeHandle — imperativ "tutqich")

  ┌────────────────────────────────────────────────────────────┐
  │ Error boundary: xato izolyatsiyasi (ilova qulamasin)        │
  │ Portal: DOM joylashuvi (modal/overlay erkin)                │
  │ Refs: imperativ kirish (deklarativ yetmaganda)              │
  └────────────────────────────────────────────────────────────┘

   Uchchovi — React'ning ODATDAGI deklarativ oqimidan TASHQARI holatlar uchun

Nega ilg'or vositalar — uchta turli muammoni uch vosita hal qiladi. Muammo 1: bitta komponentdagi xato (masalan undefined.map(), null.name) — React buni ushlamasa, butun komponentlar daraxtini o'chiradi va foydalanuvchi oq ekran ko'radi (butun ilova qulaydi) — bitta kichik widget xatosi tufayli. YechimError boundary (xatoni ushlab, faqat o'sha qismni zaxira UI bilan almashtiradi). Muammo 2: modal/tooltip/dropdown DOM ierarxiyasida "qamalgan" — masalan overflow: hidden li konteyner ichidagi modal kesiladi, yoki z-index ota element bilan cheklangan (modal boshqa narsalar ortida qoladi). YechimPortal (komponentni DOM'ning tepasiga — bodyga — chiqaradi, JSX'da qayerda turishidan qat'i nazar). Muammo 3: child DOM element yoki komponentga bevosita (imperativ) kirish kerak — fokus, scroll, media play, child input'ni dasturiy tozalash — bularni deklarativ render qila olmaydi. Yechimrefs (forwardRef, useImperativeHandle). Uch vosita umumiy mavzuga ega: ular React'ning odatdagi deklarativ oqimidan tashqari holatlarni boshqaradi (xato, DOM joylashuvi, imperativ amal) — shuning uchun "ilg'or".

2.2. Error boundary nima — xato ushlash

text
  ERROR BOUNDARY — render paytidagi XATOni "ushlab", butun ilova qulashining oldini oluvchi komponent:

  XATOSIZ:                          XATO bor (error boundary'siz):
  App                              App  (oq ekran — butun daraxt o'chdi!)
  ├─ Header                        ├─ Header 
  ├─ Widget                        ├─ Widget  (bu yerda xato: undefined.map)
  └─ Footer                        └─ Footer 

  ERROR BOUNDARY bilan (xato IZOLYATSIYALANgan):
  App 
  ├─ Header 
  ├─ <ErrorBoundary>   Widget xato  BU YERDA ushlandi  fallback UI ko'rsatadi
  │     [ Xatolik. Qayta urinish]   (App, Header, Footer ISHLAYVERADI)
  └─ Footer 

  ┌────────────────────────────────────────────────────────────┐
  │ Error boundary — try/catch'ning REACT komponent versiyasi   │
  │ (render xatosini ushlab, zaxira UI bilan almashtiradi)      │
  └────────────────────────────────────────────────────────────┘

   Error boundary'siz: bitta xato  BUTUN ilova oq ekran (production'da falokat)
   Error boundary — UI uchun try/catch (lekin faqat RENDER xatolari — 2.5)

Error boundary — render paytidagi xatoni ushlab, butun ilovaning qulashining oldini oluvchi maxsus komponent. React'ning default xulqi: agar biron komponent render paytida xato tashlasa (masalan undefined.map(), null.name), React butun komponentlar daraxtini o'chiradi — foydalanuvchi oq ekran ko'radi (bitta kichik widget xatosi butun ilovani buzadi). Error boundary buni hal qiladi: u o'z ichidagi komponentlarning render xatosini "ushlaydi" va o'rniga zaxira UI (fallback — "Xatolik yuz berdi, qayta urinib ko'ring") ko'rsatadi, qolgan ilova (xato chegarasidan tashqaridagi qismlar) ishlayveradi. Masalan, <ErrorBoundary> bilan o'ralgan Widget xato bersa, faqat o'sha joyda fallback ko'rinadi, Header/Footer normal ishlaydi. Tushunchani soddalashtirish: error boundary — bu try/catchning React komponent versiyasi (UI uchun) — render xatosini tutib, ilovani qutqaradi. Bu — production ilovada majburiy (error boundary'siz bitta kutilmagan xato butun ilovani ishdan chiqaradi). Lekin u faqat render xatolarini ushlaydi (event handler/async emas — 2.5).

2.3. Error boundary qanday yoziladi — class komponent

text
  ERROR BOUNDARY — hozircha faqat CLASS komponent (hook versiyasi yo'q — istisno):

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

    // 1. Xato yuz bersa — state'ni yangilab, fallback ko'rsatish:
    static getDerivedStateFromError(error) {
      return { hasError: true, error };       // render fazasi — UI'ni almashtiradi
    }

    // 2. Xatoni LOGLASH (Sentry'ga yuborish — 10.9):
    componentDidCatch(error, errorInfo) {
      console.error("Xato:", error, errorInfo);
      // logToSentry(error, errorInfo);        // monitoring 10.9-bob
    }

    render() {
      if (this.state.hasError) {
        return <div className="error"> Xatolik yuz berdi.</div>;   // fallback
      }
      return this.props.children;             // xato yo'q — odatdagi UI
    }
  }

  // Ishlatish:
  <ErrorBoundary><Widget /></ErrorBoundary>

   getDerivedStateFromError — UI'ni almashtiradi (render); componentDidCatch — loglaydi (side effect)
   Error boundary — yagona narsa hali CLASS talab qiladi (yoki react-error-boundary — 2.6)

Error boundary qanday yoziladi — hozircha faqat class komponent sifatida (hook versiyasi mavjud emas — bu React'ning kam istisnolaridan biri). Class ikki maxsus metodni ishlatadi: (1) static getDerivedStateFromError(error) — xato yuz berganda chaqiriladi, state'ni yangilab ({ hasError: true }) fallback UI ko'rsatishni ta'minlaydi (render fazasi — UI'ni almashtiradi); (2) componentDidCatch(error, errorInfo) — xatoni loglash uchun (konsolga, yoki Sentry kabi monitoring xizmatiga yuborish — 10.9; bu side effect, xato ma'lumotini saqlaydi). render metodida hasError bo'lsa fallback, aks holda children (odatdagi UI) qaytariladi. Ishlatish: <ErrorBoundary><Widget /></ErrorBoundary>. Ikki nuqta: (1) ikki metod ikki vazifa — getDerivedStateFromError UI'ni almashtiradi (render), componentDidCatch xatoni loglaydi (side effect, monitoring); (2) error boundary — React'ning yagona hali class talab qiladigan narsasi (yoki uni qo'lda yozish o'rniga react-error-boundary kutubxonasini ishlatasiz — funksional interfeys — 2.6). Bir marta yozib, butun loyihada qayta ishlatasiz.

2.4. Error boundary qayerga joylashtirish (granularity)

text
  ERROR BOUNDARY JOYLASHUVI — qancha "mayda" izolyatsiya kerakligiga qarab:

  1. ILDIZDA (butun ilova) — eng oxirgi himoya (hech narsa qolmasa — global fallback):
  <ErrorBoundary fallback={<GlobalError/>}>
    <App />
  </ErrorBoundary>

  2. SAHIFA/ROUTE darajasida (bir sahifa xatosi boshqasini buzmasin):
  <Route path="/dashboard" element={
    <ErrorBoundary><Dashboard /></ErrorBoundary>   // dashboard xatosi — faqat shu sahifa
  } />

  3. WIDGET darajasida (mustaqil bo'lak — masalan dashboard'dagi har grafik):
  <ErrorBoundary><ChartWidget /></ErrorBoundary>   // grafik buzilsa — qolgan widget'lar ishlaydi

  ┌────────────────────────────────────────────────────────────┐
  │ Ko'p qatlam: ildiz (global) + sahifa + muhim widget'lar     │
  │  mayda izolyatsiya = kam zarar (bitta widget ≠ butun sahifa)│
  └────────────────────────────────────────────────────────────┘

   Faqat ildizda emas — MUHIM mustaqil bo'laklarni alohida o'ra (kam zarar)
   Granularity — biznes ahamiyatiga qarab (kritik bo'lak alohida himoya)

Error boundary qayerga joylashtirish — izolyatsiya qancha "mayda" bo'lishini biznes ehtiyoji belgilaydi (granularity). Uch daraja: (1) ildizda (butun ilovani o'rab) — eng oxirgi himoya, hech narsa qolmaganda global fallback ko'rsatadi (har ilovada bo'lishi shart); (2) sahifa/route darajasida — bir sahifa xatosi boshqa sahifalarni buzmasin (<Route element={<ErrorBoundary><Dashboard/></ErrorBoundary>} /> — 11.9); (3) widget darajasida — mustaqil bo'laklar (dashboard'dagi har bir grafik, feed'dagi har post) — bittasi buzilsa qolganlari ishlayveradi. Eng yaxshi yondashuv — ko'p qatlam: ildiz (global oxirgi himoya) + sahifa + muhim mustaqil widget'lar. Ikki tamoyil: (1) faqat ildizda emas — muhim mustaqil bo'laklarni alohida o'rash kam zarar keltiradi (bitta grafik xatosi butun sahifani buzmasin); (2) granularity biznes ahamiyatiga qarab — kritik bo'lak (to'lov, asosiy kontent) o'z error boundary'siga ega bo'lsin. Bu — ishonchli, "fault-tolerant" (xatoga chidamli) UI arxitekturasi (9.6 — backend ishonchlilik tamoyillarining frontend ko'rinishi).

2.5. Error boundary nimani ushlamaydi (muhim!)

text
  ERROR BOUNDARY USHLAYDI:
   Render paytidagi xato (JSX, komponent funksiyasi ichida)
   Hayot sikli / hook xatolari (render bilan bog'liq)

  ERROR BOUNDARY USHLAMAYDI (muhim — alohida boshqarish kerak):
   EVENT HANDLER ichidagi xato (onClick, onSubmit — render emas)
     yechim: oddiy try/catch handler ichida
   ASYNC kod (setTimeout, Promise, fetch .then) xatosi
     yechim: try/catch (async/await) yoki .catch()
   SSR (server-side render) xatolari (13)
   Error boundary'ning O'ZIDAGI xato (ota error boundary ushlaydi)

  Misol — handler xatosi (error boundary USHLAMAYDI):
  <button onClick={() => { throw new Error("xato"); }}>   //  error boundary tutmaydi
  //  try { ... } catch(e) { setError(e); }              // qo'lda

   Error boundary — faqat RENDER xatolari uchun (deklarativ oqim)
   Event/async xatolari — try/catch + state (qo'lda) yoki global window.onerror

Error boundary nimani ushlamaydi — bu eng muhim, lekin eng ko'p e'tibordan chetda qoladigan nuqta. Error boundary ushlaydi: render paytidagi xato (JSX, komponent funksiyasi ichida), hayot sikli/hook bilan bog'liq xatolar. Error boundary ushlamaydi: (1) event handler ichidagi xato (onClick, onSubmit — bular render emas, foydalanuvchi harakati natijasida ishlaydi); (2) async kod xatosi (setTimeout, Promise, fetch().then — render fazasidan tashqarida); (3) SSR xatolari (13-QISM); (4) error boundary'ning o'zidagi xato (uni faqat ota error boundary ushlaydi). Demak event handler va async kod xatolarini qo'lda boshqarish kerak: handler ichida try/catch (yoki async uchun try/catch + await/.catch()), va xatoni state'ga saqlab ko'rsatish. Ikki muhim xulosa: (1) error boundary — faqat render (deklarativ oqim) xatolari uchun; (2) event/async xatolari — try/catch + state (yoki global window.onerror/unhandledrejection — monitoring uchun). Bu cheklovni bilmaslik — "error boundary qo'ydim, lekin xato ushlanmadi" degan keng tuzoqning sababi. To'liq xato boshqaruvi — error boundary (render) + try/catch (event/async) birgalikda.

2.6. react-error-boundary kutubxonasi

text
  react-error-boundary — error boundary'ning funksional, kuchli interfeysi (qo'lda class yozish o'rniga):
  npm install react-error-boundary

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

  function Fallback({ error, resetErrorBoundary }) {
    return (
      <div role="alert">
        <p>Xatolik: {error.message}</p>
        <button onClick={resetErrorBoundary}>Qayta urinish</button>   {/* RESET — qayta ishga tushir */}
      </div>
    );
  }

  <ErrorBoundary
    FallbackComponent={Fallback}
    onError={(error, info) => logToSentry(error, info)}   // loglash 10.9-bob
    onReset={() => { /* state'ni tozalash */ }}            // reset paytida
  >
    <Widget />
  </ErrorBoundary>

  AFZALLIKLARI:
   resetErrorBoundary — xatodan "tiklanish" (qayta urinish — qo'lda class'da qiyin)
   useErrorBoundary hook — async xatoni ham boundary'ga "uzatish"
   Funksional, qulay, sinalgan (qo'lda class yozish shart emas)

   react-error-boundary — amalda STANDART (qo'lda class o'rniga — reset/hook qulay)
   resetErrorBoundary — foydalanuvchi xatodan keyin qayta urina oladi (UX)

react-error-boundary — error boundary'ning funksional, kuchli interfeysi (qo'lda class yozish o'rniga amaliyotda standart). <ErrorBoundary> komponenti uch asosiy prop oladi: FallbackComponent (xato paytida ko'rsatiladigan komponent — error va resetErrorBoundary props'larini oladi), onError (xatoni loglash — Sentry'ga — 10.9), onReset (reset paytida state tozalash). Afzalliklari: (1) resetErrorBoundary — xatodan "tiklanish" (foydalanuvchi "Qayta urinish" bossa, error boundary state'ini tozalab, qaytadan render qiladi — bu qo'lda class'da qiyin, lekin muhim UX); (2) useErrorBoundary hook — async/event handler xatosini ham boundary'ga "uzatish" (2.5 cheklovini qisman aylanib o'tish); (3) funksional, qulay, sinalgan. Ikki nuqta: (1) amalda react-error-boundary ishlatiladi (qo'lda class yozish o'rniga — reset/hook qulayligi tufayli); (2) resetErrorBoundary — foydalanuvchi xatodan keyin qayta urina olishini ta'minlaydi (oq ekran o'rniga — "qayta urinish" tugmasi — yaxshi UX). Bu kutubxona error boundary'ni production'da qulay va to'liq qiladi.

2.7. Portals nima — DOM teleport

text
  PORTAL — komponentni JSX'da bir joyda yozib, DOM'ning BOSHQA joyiga render qilish:

  JSX (komponentlar daraxti):          DOM (haqiqiy):
  <App>                                <body>
    <div className="card">               <div id="root">
      <Modal>...</Modal>  ──teleport──┐   <div class="card"></div>    Modal BU YERDA EMAS
    </div>                            │   </div>
  </App>                             └ <div class="modal">...</div>   Modal SHU YERDA (body'da!)
                                       </body>

  NEGA KERAK (modal/tooltip/dropdown):
   Modal ota <div style="overflow:hidden"> ichida  KESILADI
   Modal ota z-index ichida  boshqa narsalar ortida qoladi
   Portal  modal body'ning tepasiga  overflow/z-index ota MUAMMOLARIDAN OZOD

  ┌────────────────────────────────────────────────────────────┐
  │ JSX joyi: card ichida (mantiqiy ota-bola — props/context bor) │
  │ DOM joyi: body'da (vizual — overflow/z-index dan ozod)      │
  └────────────────────────────────────────────────────────────┘

   Portal — JSX'da "bola", DOM'da "tepada" (mantiq bilan ko'rinish ajratiladi)
   Modal/tooltip/dropdown — portal'siz overflow/z-index'da buziladi (klassik muammo)

Portals — komponentni JSX'da bir joyda yozib (mantiqiy joylashuv), uni DOM'ning boshqa joyiga render qilish imkoni. Misol: Modal komponenti JSX'da <div className="card"> ichida yozilgan bo'lsa ham, portal bilan u haqiqiy DOM'da <body> darajasida render bo'ladi (card ichida emas). Nega kerak — modal, tooltip, dropdown kabi "qalqib chiquvchi" (overlay) UI uchun: agar modal ota element overflow: hidden ichida bo'lsa, u kesiladi (ko'rinmaydi); agar ota z-index ichida bo'lsa, modal boshqa narsalar ortida qoladi; portal modal'ni bodyning tepasiga chiqarib, bu overflow/z-index muammolaridan ozod qiladi. Eng muhim haqiqat: portal mantiq (JSX) va ko'rinish (DOM) ni ajratadi — modal JSX'da card'ning bolasi (shuning uchun props/context oladi — 2.9), lekin DOM'da body'da (vizual jihatdan erkin). Ikki nuqta: (1) portal — JSX'da "bola", DOM'da "tepada"; (2) modal/tooltip/dropdown portal'siz albatta overflow/z-index muammolariga duch keladi (klassik xato) — shuning uchun ular doim portal bilan quriladi. Portal — overlay UI'ning poydevori.

2.8. createPortal — sintaksis va ishlatilishi

text
  createPortal(JSX, DOM_element) — JSX'ni berilgan DOM elementga render qiladi:

  import { createPortal } from "react-dom";

  function Modal({ children, onClose }) {
    return createPortal(
      <div className="modal-backdrop" onClick={onClose}>
        <div className="modal" onClick={e => e.stopPropagation()}>   {/* ichga bosish yopmasin */}
          {children}
          <button onClick={onClose}>×</button>
        </div>
      </div>,
      document.body                  //  qayerga render (body — yoki maxsus #modal-root)
    );
  }

  // index.html'da maxsus konteyner (ixtiyoriy, lekin tartibli):
  // <div id="modal-root"></div>
  // createPortal(<...>, document.getElementById("modal-root"))

  ISHLATISH — oddiy komponent kabi (JSX'da card ichida bo'lsa ham — DOM'da body'da):
  {isOpen && <Modal onClose={() => setOpen(false)}>Modal kontenti</Modal>}

   createPortal(JSX, target) — react-dom'dan; target — haqiqiy DOM element
   Konteyner: document.body yoki maxsus #modal-root (tartibli — index.html'da)

createPortal — portal'ni amalga oshiruvchi funksiya (react-dom'dan): createPortal(JSX, DOM_element) — birinchi argument JSX (render qilinadigan kontent), ikkinchi argument haqiqiy DOM element (qayerga render qilinadi — odatda document.body yoki maxsus #modal-root). Modal misolida: createPortal(<div className="modal-backdrop">...</div>, document.body) — modal'ning butun JSX'i bodyga render bo'ladi. Tartibli yondashuv — index.html'da maxsus <div id="modal-root"></div> yaratib, modal'larni o'sha yerga render qilish (document.getElementById("modal-root")) — bu modal'larni #root'dan (asosiy ilova) ajratib turadi. Ichki komponentda e.stopPropagation() — modal ichiga bosganda backdrop'ning onClose'i ishlamasin (modal yopilmasin — faqat tashqariga bosganda yopilsin). Ishlatish — oddiy komponent kabi ({isOpen && <Modal>...</Modal>}), JSX'da card ichida bo'lsa ham DOM'da body'da. Ikki nuqta: (1) createPortalreact-dom'dan import qilinadi (React'dan emas); (2) konteyner sifatida document.body (oddiy) yoki maxsus #modal-root (tartibli) — ikkalasi ham ishlaydi. Bu — modal komponentining asosiy tuzilishi.

2.9. Portal va event bubbling — React tree vs DOM tree

text
  NOZIK HAQIQAT: portal DOM'da body'da bo'lsa ham, EVENT'lar React DARAXTI bo'yicha bubbling qiladi!

  JSX (React daraxti):              DOM (haqiqiy):
  <Parent onClick={handle}>         <div id="root">
    <Modal>  (portal)                 <Parent>...</Parent>
      <button>Bosish</button>       </div>
    </Modal>                        <div class="modal">          DOM'da body'da
  </Parent>                           <button>Bosish</button>    lekin...
                                    </div>

  Modal ichidagi button bosilsa:
   DOM bo'yicha: body'da (Parent'dan tashqarida)
   LEKIN React event bubbling: Parent.onClick ISHLAYDI! (React DARAXTI bo'yicha — JSX'da bola)

  ┌────────────────────────────────────────────────────────────┐
  │ DOM joyi: body (vizual) | Event bubbling: React daraxti (JSX)│
  │  portal ichidagi hodisa OTA komponentga (JSX'dagi) ko'tariladi│
  └────────────────────────────────────────────────────────────┘

   Portal ichidagi event — JSX'dagi OTAga bubbling qiladi (DOM joyiga emas)
   Bu KUTILMAGAN bo'lishi mumkin (modal ichidagi click  ota handler) — bilib turish kerak

Portal va event bubbling — portal'ning eng nozik, ko'pincha kutilmagan xususiyati. Portal komponent DOM'da bodyda render bo'lsa ham (Parent'dan tashqarida), uning ichidagi hodisalar (click, keydown) React komponentlar daraxti bo'yicha bubbling qiladi (DOM daraxti bo'yicha emas — 11.1: 2.6). Ya'ni, modal ichidagi tugma bosilsa, DOM jihatdan u bodyda (Parent'dan tashqarida), lekin React event bubbling JSX strukturasini kuzatadi — shuning uchun Parent.onClick ishlaydi (chunki JSX'da Modal — Parent'ning bolasi). Bu — React'ning ataylab qilgan dizayni (mantiqiy joylashuvni saqlash): portal komponent JSX'da qayerda bo'lsa, hodisalari ham o'sha kontekstga ko'tariladi. Ikki muhim nuqta: (1) DOM joyi (body — vizual) va event bubbling (React daraxti — JSX) farq qiladi — bu portal'ning o'ziga xosligi; (2) bu kutilmagan bo'lishi mumkin — masalan modal ichidagi click ota komponentning click handler'ini ishga tushirishi mumkin (agar ota'da onClick bo'lsa) — shuning uchun buni bilib turish va kerak bo'lsa stopPropagation bilan boshqarish kerak. Bu tushuncha portal bilan murakkab UI (modal ichida forma, ichma-ich overlay) qurganda muhim.

2.10. useRef takror va forwardRef — ref uzatish

text
  useRef (11.5: 2.12 takror) — DOM elementga yoki render'siz qiymatga "ishora":
  const inputRef = useRef(null);
  <input ref={inputRef} />        inputRef.current.focus();

  MUAMMO: ref'ni O'Z KOMPONENTINGGA bersa bo'lmaydi (oddiy holatda):
  function MyInput() { return <input />; }
  <MyInput ref={ref} />           //  ref MyInput'ga (DOM'ga emas) — ishlamaydi (ogohlantirish)

  forwardRef — ref'ni komponent ICHIDAGI DOM elementga "uzatish" (React 18 va undan oldin):
  const MyInput = forwardRef(function MyInput(props, ref) {
    return <input ref={ref} {...props} />;   //  ref ichidagi input'ga uzatildi
  });
  <MyInput ref={ref} />           //  endi ref haqiqiy <input>'ga (ref.current.focus() ishlaydi)

  ┌────────────────────────────────────────────────────────────┐
  │ forwardRef: ota'ning ref'ini komponent ICHIDAGI DOM'ga uzatadi│
  │ (custom input/button kutubxonasida ZARUR — ota fokus qila olsin)│
  └────────────────────────────────────────────────────────────┘

   forwardRef — custom komponentga ref berish uchun (React 18-); React 19'da SODDAROQ (2.11)
   Kutubxona komponentlari (custom Input) forwardRef bilan ref'ni "ochadi"

useRef takror va forwardRef — ref'ni komponentlar orqali uzatish. useRef (11.5: 2.12) — DOM elementga yoki render'siz qiymatga ishora (const inputRef = useRef(null); <input ref={inputRef} />; inputRef.current.focus()). Muammo: ref'ni o'z custom komponentingga bersa bo'lmaydi (oddiy holatda) — <MyInput ref={ref} /> ishlamaydi, chunki ref MyInput funksiyasiga beriladi (DOM'ga emas), React ogohlantiradi. Yechim (React 18 va undan oldin) — forwardRef: u komponentni o'rab, ota'ning ref'ini komponent ichidagi DOM elementga "uzatadi": forwardRef(function MyInput(props, ref) { return <input ref={ref} {...props} /> }) — endi ref haqiqiy <input>'ga ulanadi (ref.current.focus() ishlaydi). Nega kerak: custom/kutubxona komponentlari (<Input>, <Button>, <Select>) — ota komponent ularning ichidagi DOM elementga kirishi kerak bo'lganda (fokus, scroll, o'lchov). Ikki nuqta: (1) forwardRef — custom komponentga ref berishning React 18- usuli; (2) React 19'da buni qilish ancha soddaroq bo'ldi (ref oddiy prop — forwardRef ko'p holda kerak emas — 2.11). Kutubxona komponentlari ref'ni shu tarzda "ochadi".

Ref va state farqi — bu ikki mexanizm tez-tez chalkashtiriladi, lekin ular butunlay boshqacha maqsad uchun (11.5: 2.12 chuqurroq). Asosiy farq: state o'zgarganda React komponentni qayta render qiladi (UI yangilanadi), ref (ref.current) o'zgarganda render bo'lmaydi (UI o'zgarmaydi).

text
  REF vs STATE — qachon qaysi biri:

                  STATE (useState)              REF (useRef)
  O'zgarish       qayta render QILADI           qayta render QILMAYDI
  Qiymat          "snapshot" (render'da qotgan) "mutable" (darrov yangilanadi)
  Maqsad          UI'da ko'rinadigan ma'lumot   render'ga ta'sir qilmaydigan qiymat
  Misol           forma qiymati, ochiq/yopiq    DOM element, timer id, oldingi qiymat

   STATE ishlat: qiymat ekranda KO'RINSA (o'zgarishi UI'ni yangilashi kerak)
   REF ishlat:   qiymat render'ga TA'SIR qilmasa (DOM tutqichi, taymer id, sanoq)

  //  NOTO'G'RI — ref'ni UI qiymati sifatida (o'zgarishi ko'rinmaydi):
  const count = useRef(0);
  count.current++;                 // render bo'lmaydi  ekranda 0 qoladi (xato)

  //  TO'G'RI — ko'rinadigan qiymat  state:
  const [count, setCount] = useState(0);
  setCount(c => c + 1);            // render bo'ladi  ekran yangilanadi

   State = ko'rinadigan (render); ref = ko'rinmaydigan, o'zgaruvchan (render'siz)

Ref va state farqi — bir necha ko'rinishga o'xshasa ham, ular boshqacha vazifa bajaradi. state — komponentning ko'rinadigan holati: o'zgarganda React qayta render qiladi (UI yangilanadi), va qiymat har render uchun "snapshot" (o'sha render davomida qotgan — 11.2). refrender'ga ta'sir qilmaydigan, o'zgaruvchan (mutable) qiymat idishi: ref.currentni o'zgartirish qayta renderni keltirmaydi (UI o'zgarmaydi), lekin qiymat darrov yangilanadi va renderlar orasida saqlanadi. Qoida: agar qiymat ekranda ko'rinsa (o'zgarishi UI'ni yangilashi kerak) — state; agar qiymat render'ga ta'sir qilmasa (DOM element tutqichi, setTimeout/setInterval id'si, oldingi qiymatni eslab qolish, tashqi kutilmagan sanoq) — ref. Klassik xato — sanoqchini useRef bilan yuritib, ekranda yangilanishini kutish: count.current++ render qilmaydi, shuning uchun ekran o'zgarmaydi (bunda useState kerak). Aksincha, setInterval id'sini useStateda saqlash — har taymer id o'zgarishi keraksiz render keltiradi (bunda useRef to'g'ri). Bir jumlada: state — UI'da ko'rinadigan (render qiluvchi) ma'lumot; ref — ko'rinmaydigan, o'zgaruvchan (render qilmaydigan) qiymat. Bu farqni tushunish forwardRef/useImperativeHandleni to'g'ri ishlatishning asosidir (ref imperativ tutqich, deklarativ state emas).

2.11. React 19 — ref as prop

text
  REACT 19 (joriy) — ref endi ODDIY PROP (forwardRef KO'P HOLDA kerak emas):

  // React 18 (forwardRef bilan):
  const MyInput = forwardRef(function MyInput(props, ref) {
    return <input ref={ref} {...props} />;
  });

  // React 19 (ref oddiy prop — soddaroq):
  function MyInput({ ref, ...props }) {        // ref'ni to'g'ridan prop sifatida ol
    return <input ref={ref} {...props} />;     // va uzat
  }
  <MyInput ref={inputRef} />                   //  ishlaydi (forwardRef'siz)

  ┌────────────────────────────────────────────────────────────┐
  │ React 18: forwardRef(fn) — ikkinchi argument ref            │
  │ React 19: function C({ ref, ...props }) — ref oddiy prop    │
  └────────────────────────────────────────────────────────────┘

   React 19 — ref oddiy prop  forwardRef boilerplate'i yo'qoladi (soddaroq)
   Eski kod (forwardRef) hali ishlaydi; yangi kod — ref prop sifatida (2026)

React 19 — ref as prop — ref uzatishni sezilarli soddalashtirgan yangilik. React 18 va undan oldin custom komponentga ref berish uchun forwardRef kerak edi (komponentni o'rab, ikkinchi argument sifatida ref olib). React 19'da ref oddiy propga aylandi: komponent uni boshqa props kabi to'g'ridan oladi — function MyInput({ ref, ...props }) { return <input ref={ref} {...props} /> } — va <MyInput ref={inputRef} /> to'g'ridan ishlaydi (forwardRefsiz). Bu — kichik, lekin yoqimli o'zgarish: forwardRefning "boilerplate" (qo'shimcha o'rovchi kod)i yo'qoladi, kod toza va oddiy bo'ladi. Ikki amaliy nuqta: (1) eski kod (forwardRef bilan) React 19'da ham ishlayveradi (orqaga moslik), shuning uchun mavjud kodni o'zgartirish shart emas; (2) yangi kod (2026'dan) ref'ni oddiy prop sifatida yozadi (forwardRefsiz). Bu bobda ikkala usulni ko'ramiz, chunki amalda ikkalasi ham uchraydi (eski kutubxonalar forwardRef, yangilari ref prop). React'ning umumiy yo'nalishi — soddalik tomon (hooklar bilan boshlangan jarayon davom etadi).

2.12. useImperativeHandle — imperativ API ochish

text
  useImperativeHandle — komponent o'z ref'i orqali MAXSUS API (metodlar) ochadi:

  function VideoPlayer({ ref, src }) {         // React 19 (yoki forwardRef — 18)
    const videoRef = useRef(null);

    useImperativeHandle(ref, () => ({          //  otaga OCHILADIGAN metodlar:
      play: () => videoRef.current.play(),
      pause: () => videoRef.current.pause(),
      reset: () => { videoRef.current.currentTime = 0; },
    }));                                        // ota faqat SHU 3 metodni ko'radi (ichkini emas)

    return <video ref={videoRef} src={src} />;
  }

  // Ota — maxsus API'ni ishlatadi (DOM'ga to'g'ridan emas — boshqarilgan):
  function App() {
    const playerRef = useRef(null);
    return (
      <>
        <VideoPlayer ref={playerRef} src="..." />
        <button onClick={() => playerRef.current.play()}>▶</button>   {/* ochilgan metod */}
        <button onClick={() => playerRef.current.reset()}>⟲</button>
      </>
    );
  }

   useImperativeHandle — DOM'ni to'g'ridan ochish o'rniga, BOSHQARILGAN API (faqat kerakli metod)
   Kam ishlatiladi (imperativ — deklarativ afzal); video/forma/animatsiya kutubxonasida foydali

useImperativeHandle — komponent o'z ref'i orqali ota'ga maxsus API (tanlangan metodlar) ochishiga imkon beradi (butun DOM elementni ochish o'rniga). useImperativeHandle(ref, () => ({ play, pause, reset })) — ota komponent ref.current orqali faqat shu uch metodni ko'radi (ichki videoRef yoki butun DOM elementni emas). Misolda VideoPlayer o'z ichki <video> elementini yashiradi va faqat play/pause/reset metodlarini ochadi — ota playerRef.current.play() bilan boshqaradi (DOM'ga to'g'ridan tegmasdan). Nega kerak — bu inkapsulyatsiya 9.1-bob: komponent o'z ichki ishlashini yashiradi va faqat boshqarilgan, ataylab tanlangan interfeysni ochadi (ota butun DOM'ni o'zgartira olmaydi, faqat ruxsat etilgan amallarni qiladi). Ikki nuqta: (1) useImperativeHandle — DOM'ni to'g'ridan ochish o'rniga boshqarilgan API (faqat kerakli metodlar — xavfsizroq, toza); (2) kam ishlatiladi — imperativ yondashuv (deklarativ afzal), lekin video/audio pleer, forma (focus/reset/validate), animatsiya, scroll boshqaruvi kabi imperativ tabiatli komponentlarda foydali. Bu — kutubxona komponentlari dizaynining ilg'or vositasi.

2.13. Callback refs va kombinatsiya

text
  CALLBACK REF — ref obyekt o'rniga FUNKSIYA (element ulanganda/uzilganda chaqiriladi):
  <div ref={(node) => {
    if (node) { /* element ulandi — o'lchash, kuzatish */ }
    else { /* element uzildi — tozalash */ }
  }} />

  QACHON callback ref (useRef o'rniga):
   Element ulanganda DARROV biror ish (o'lchash, scroll, observer ulash)
   Dinamik element (ro'yxat — har element ref'ini yig'ish)

  Misol — o'lchash (element ulanganda balandlikni olish):
  const [height, setHeight] = useState(0);
  const measureRef = useCallback((node) => {
    if (node) setHeight(node.getBoundingClientRect().height);   // ulanganda o'lcha
  }, []);
  <div ref={measureRef}>...</div>

  REF KOMBINATSIYA (bitta elementga 2 ref — masalan o'z ref + kutubxona ref):
  const setRefs = (node) => { myRef.current = node; libRef(node); };   // ikkalasiga

   Callback ref — element ulanish/uzilishiga reaksiya (useRef faqat .current saqlaydi)
   O'lchash/observer ulash uchun callback ref afzal (useEffect'dan aniqroq vaqt)

Callback refs va kombinatsiya — ref'ning ilg'or shakllari. Callback refrefga obyekt (useRef) o'rniga funksiya berish: <div ref={(node) => {...}} /> — React bu funksiyani element DOM'ga ulanganda (node — element) va uzilganda (nodenull) chaqiradi. Qachon callback ref kerak (useRef o'rniga): (1) element ulanganda darrov biror ish qilish kerak bo'lganda (o'lchash — getBoundingClientRect, scroll, ResizeObserver/IntersectionObserver ulash); (2) dinamik elementlar (ro'yxat — har elementning ref'ini yig'ish). Misol — element balandligini ulanganda olish: const measureRef = useCallback((node) => { if (node) setHeight(node.getBoundingClientRect().height) }, []). Ref kombinatsiya — bitta elementga ikkita ref ulash (masalan o'z ref + kutubxona ref): const setRefs = (node) => { myRef.current = node; libRef(node) }. Ikki nuqta: (1) callback ref — element ulanish/uzilishiga reaksiya beradi (useRef faqat .currentni passiv saqlaydi); (2) o'lchash yoki observer ulash uchun callback ref useEffect'dan aniqroq vaqt beradi (element ulangan aniq lahzada ishlaydi). Bu — o'lchovga asoslangan UI (animatsiya, virtualizatsiya, joylashuv hisoblash) uchun foydali ilg'or texnika.

2.14. Birlashtirish — professional modal komponenti

text
  HAMMA VOSITANI BIRGA — production modal (portal + ref + a11y + error boundary):

  XUSUSIYATLAR (professional modal):
   Portal — body'ga render (overflow/z-index ozod — 2.7, 2.8)
   Backdrop — tashqariga bossa yopiladi (stopPropagation ichda — 2.8)
   Escape — ESC tugmasi yopadi (keydown listener + cleanup — 11.5)
   Focus trap — fokus modal ichida qoladi (a11y — Tab modal'dan chiqmasin)
   Scroll lock — modal ochiq paytda fon scroll bo'lmasin (body overflow hidden)
   Auto-focus — ochilganda modal'ga fokus (ref — 2.10)
   aria-modal, role="dialog" — ekran o'quvchi (a11y — 1.9)
   Error boundary — modal ichidagi xato butun ilovani buzmasin 2.4-bob

  ┌────────────────────────────────────────────────────────────┐
  │ Modal = portal (joylashuv) + ref (fokus) + listener (esc) + │
  │ a11y (dialog) + error boundary (izolyatsiya)                │
  └────────────────────────────────────────────────────────────┘

   Professional modal — bu bobning BARCHA vositasini birlashtiradi (Misol 14)
   Amalda — Radix UI / Headless UI (tayyor, a11y'li) — lekin TUSHUNISH uchun o'zingiz yozing

Birlashtirish — professional modal — bu bobning barcha vositasini birga ishlatadigan namuna. Production darajasidagi modal quyidagi xususiyatlarni birlashtiradi: portal (body'ga render — overflow/z-index ozod — 2.7, 2.8); backdrop (orqa fon — tashqariga bossa yopiladi, ichga bossa yopilmaydi — stopPropagation); Escape (ESC tugmasi yopadi — keydown listener + cleanup — 11.5: 2.7); focus trap (fokus modal ichida qoladi — Tab modal'dan chiqmasin — a11y); scroll lock (modal ochiq paytda fon scroll bo'lmasin — body overflow hidden); auto-focus (ochilganda modal'ga fokus — ref — 2.10); a11y (role="dialog", aria-modal="true" — ekran o'quvchi uchun — 1.9); error boundary (modal ichidagi xato butun ilovani buzmasin — 2.4). Ikki nuqta: (1) professional modal — bu bobning barcha tushunchasini (portal, ref, listener, a11y, error boundary) birlashtiradi (Misol 14); (2) amalda ko'p loyiha tayyor, a11y'li kutubxona ishlatadi — Radix UI, Headless UI (modal, dropdown, tooltip — to'liq a11y va edge-case'lar hisobga olingan) — lekin uni o'zingiz yozib ko'rish mexanizmni chuqur tushunish uchun bebaho (kutubxonani qora quti sifatida emas, bilib ishlatasiz). Modal — overlay UI'ning eng to'liq namunasi.


3. Sintaksis — tez ma'lumotnoma

text
ERROR BOUNDARY 2.3-bob:  class EB extends React.Component { static getDerivedStateFromError(e){...}
                       componentDidCatch(e,info){...} render(){ return hasError ? fallback : children } }
react-error-bdry 2.6-bob: <ErrorBoundary FallbackComponent={F} onError={...} onReset={...}>
PORTAL 2.8-bob:          createPortal(<JSX/>, document.body)   // react-dom'dan
forwardRef 2.10-bob:     const C = forwardRef((props, ref) => <input ref={ref}/>)   // React 18-
REF PROP 2.11-bob:       function C({ ref, ...props }) { return <input ref={ref}/> }  // React 19
IMPERATIVE 2.12-bob:     useImperativeHandle(ref, () => ({ play, pause, reset }))
CALLBACK REF 2.13-bob:   <div ref={(node) => { if(node) measure(node) }} />

4. Batafsil kod namunalari

Misol 1 — Asosiy error boundary (2.3)

jsx
class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };               // xato  fallback ko'rsat
  }
  componentDidCatch(error, errorInfo) {
    console.error("Ushlandi:", error, errorInfo);   // loglash (10.9 — Sentry)
  }
  render() {
    if (this.state.hasError) {
      return <div className="error"> Xatolik yuz berdi.</div>;
    }
    return this.props.children;
  }
}

// Ishlatish:
<ErrorBoundary>
  <BuggyWidget />          {/* xato bersa — faqat shu yerda fallback, qolgani ishlaydi */}
</ErrorBoundary>

Misol 2 — Error boundary fallback + reset (2.3, 2.6)

jsx
class ErrorBoundary extends React.Component {
  state = { hasError: false, error: null };
  static getDerivedStateFromError(error) { return { hasError: true, error }; }

  reset = () => this.setState({ hasError: false, error: null });   // qayta urinish

  render() {
    if (this.state.hasError) {
      return (
        <div role="alert" className="error-box">
          <p>Xatolik: {this.state.error?.message}</p>
          <button onClick={this.reset}>Qayta urinish</button>      {/* reset */}
        </div>
      );
    }
    return this.props.children;
  }
}

Misol 3 — Granular error boundaries (2.4)

jsx
function Dashboard() {
  return (
    <div className="dashboard">
      {/* Har widget alohida — bittasi buzilsa qolgani ishlaydi */}
      <ErrorBoundary><SalesChart /></ErrorBoundary>
      <ErrorBoundary><UsersWidget /></ErrorBoundary>
      <ErrorBoundary><RevenueWidget /></ErrorBoundary>
    </div>
  );
}
//  SalesChart buzilca — faqat o'sha joyda "Xatolik", Users/Revenue ISHLAYDI (2.4)

Misol 4 — react-error-boundary kutubxonasi (2.6)

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

function Fallback({ error, resetErrorBoundary }) {
  return (
    <div role="alert">
      <p>Xatolik: {error.message}</p>
      <button onClick={resetErrorBoundary}>Qayta urinish</button>
    </div>
  );
}

function App() {
  return (
    <ErrorBoundary
      FallbackComponent={Fallback}
      onError={(error, info) => console.error(error, info)}   // Sentry 10.9-bob
      onReset={() => window.location.reload()}                // reset paytida
    >
      <Widget />
    </ErrorBoundary>
  );
}
//  Funksional, reset bilan — qo'lda class o'rniga standart (2.6)

Misol 5 — Error boundary + lazy (11.8 bilan — 2.4)

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

const Dashboard = lazy(() => import("./Dashboard"));

function App() {
  return (
    <ErrorBoundary FallbackComponent={Fallback}>   {/* yuklanish/render xatosi */}
      <Suspense fallback={<Spinner />}>            {/* yuklanish kutishi */}
        <Dashboard />
      </Suspense>
    </ErrorBoundary>
  );
}
//  ErrorBoundary > Suspense > Lazy — chunk yuklanmasa "qayta urinish" (11.8: 2.8)

Misol 6 — Event/async xato (boundary USHLAMAYDI — 2.5)

jsx
function Form() {
  const [error, setError] = useState(null);

  async function handleSubmit(e) {
    e.preventDefault();
    try {
      await api.submit();                    // async — error boundary USHLAMAYDI (2.5)
    } catch (err) {
      setError(err.message);                 //  qo'lda try/catch + state
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      {error && <p className="error">{error}</p>}
      <button>Yuborish</button>
    </form>
  );
}
//  Event handler / async xato — error boundary EMAS, qo'lda try/catch (2.5)

Misol 7 — Asosiy portal modal (2.8)

jsx
import { createPortal } from "react-dom";

function Modal({ children, onClose }) {
  return createPortal(
    <div className="modal-backdrop" onClick={onClose}>          {/* tashqariga bosish yopadi */}
      <div className="modal" onClick={e => e.stopPropagation()}> {/* ichga bosish yopmaydi 2.8-bob */}
        {children}
        <button onClick={onClose} aria-label="Yopish">×</button>
      </div>
    </div>,
    document.body                            //  body'ga render (overflow/z-index ozod — 2.7)
  );
}

function App() {
  const [open, setOpen] = useState(false);
  return (
    <div style={{ overflow: "hidden" }}>     {/* modal portal'siz bu yerda KESILARDI */}
      <button onClick={() => setOpen(true)}>Modal ochish</button>
      {open && <Modal onClose={() => setOpen(false)}>Salom!</Modal>}
    </div>
  );
}
//  Portal tufayli modal overflow:hidden ota ichida bo'lsa ham TO'LIQ ko'rinadi (2.7)

Misol 8 — Modal: Escape + scroll lock + auto-focus (2.14)

jsx
function Modal({ children, onClose }) {
  const modalRef = useRef(null);

  useEffect(() => {
    // ESC bilan yopish
    const onKey = (e) => e.key === "Escape" && onClose();
    document.addEventListener("keydown", onKey);

    // Scroll lock (modal ochiq — fon scroll bo'lmasin)
    document.body.style.overflow = "hidden";

    // Auto-focus (ochilganda modal'ga fokus — a11y)
    modalRef.current?.focus();

    return () => {                           // cleanup (11.5: 2.7)
      document.removeEventListener("keydown", onKey);
      document.body.style.overflow = "";     // scroll qaytar
    };
  }, [onClose]);

  return createPortal(
    <div className="modal-backdrop" onClick={onClose}>
      <div
        className="modal"
        ref={modalRef}
        tabIndex={-1}                        // fokuslanadigan qilish
        role="dialog"                        // a11y (1.9)
        aria-modal="true"
        onClick={e => e.stopPropagation()}
      >
        {children}
      </div>
    </div>,
    document.body
  );
}

Misol 9 — Tooltip portal (2.7, 2.8)

jsx
function Tooltip({ text, children }) {
  const [coords, setCoords] = useState(null);

  return (
    <>
      <span
        onMouseEnter={(e) => {
          const r = e.currentTarget.getBoundingClientRect();
          setCoords({ x: r.left, y: r.bottom });   // joylashuvni o'lcha 2.13-bob
        }}
        onMouseLeave={() => setCoords(null)}
      >
        {children}
      </span>
      {coords && createPortal(                       // portal — body'ga (z-index ozod)
        <div className="tooltip" style={{ position: "fixed", left: coords.x, top: coords.y }}>
          {text}
        </div>,
        document.body
      )}
    </>
  );
}
//  Tooltip portal'siz ota overflow/z-index'da buzilardi (2.7)

Misol 10 — forwardRef (custom input — 2.10)

jsx
import { forwardRef } from "react";

const TextInput = forwardRef(function TextInput({ label, ...props }, ref) {
  return (
    <div className="field">
      <label>{label}</label>
      <input ref={ref} {...props} />         {/* ref ichidagi input'ga uzatildi 2.10-bob */}
    </div>
  );
});

function Form() {
  const inputRef = useRef(null);
  useEffect(() => { inputRef.current?.focus(); }, []);   // mount'da fokus

  return <TextInput ref={inputRef} label="Ism" />;        // ref haqiqiy input'ga
}
//  forwardRef'siz <TextInput ref={...}> ishlamasdi (ref komponentga, DOM'ga emas — 2.10)

Misol 11 — React 19 ref as prop (2.11)

jsx
// React 19 — forwardRef KERAK EMAS (ref oddiy prop):
function TextInput({ label, ref, ...props }) {   // ref'ni to'g'ridan prop sifatida ol
  return (
    <div className="field">
      <label>{label}</label>
      <input ref={ref} {...props} />               // va uzat
    </div>
  );
}

function Form() {
  const inputRef = useRef(null);
  return <TextInput ref={inputRef} label="Email" />;   //  ishlaydi (forwardRef'siz — 2.11)
}
//  React 19: ref oddiy prop  forwardRef boilerplate'i yo'q (soddaroq — 2.11)

Misol 12 — useImperativeHandle (video pleer API — 2.12)

jsx
function VideoPlayer({ src, ref }) {           // React 19 (yoki forwardRef — 18)
  const videoRef = useRef(null);

  useImperativeHandle(ref, () => ({            // otaga ochiladigan metodlar (2.12)
    play: () => videoRef.current.play(),
    pause: () => videoRef.current.pause(),
    reset: () => { videoRef.current.currentTime = 0; },
  }));

  return <video ref={videoRef} src={src} width="400" />;
}

function App() {
  const playerRef = useRef(null);
  return (
    <div>
      <VideoPlayer ref={playerRef} src="/video.mp4" />
      <button onClick={() => playerRef.current.play()}>▶ Play</button>
      <button onClick={() => playerRef.current.pause()}> Pause</button>
      <button onClick={() => playerRef.current.reset()}>⟲ Reset</button>
    </div>
  );
}
//  Ota faqat play/pause/reset ko'radi (ichki video DOM'ni emas — inkapsulyatsiya — 2.12)

Misol 13 — Callback ref bilan o'lchash (2.13)

jsx
function MeasuredBox() {
  const [size, setSize] = useState({ width: 0, height: 0 });

  // Element ulanganda darrov o'lcha (callback ref — 2.13)
  const measureRef = useCallback((node) => {
    if (node) {
      const rect = node.getBoundingClientRect();
      setSize({ width: rect.width, height: rect.height });
    }
  }, []);

  return (
    <div ref={measureRef} className="box">
      O'lcham: {size.width.toFixed(0)} × {size.height.toFixed(0)} px
    </div>
  );
}
//  Callback ref — element ulanganda DARROV (useEffect'dan aniqroq vaqt — 2.13)

Misol 14 — To'liq professional modal (hammasi birga — 2.14)

jsx
import { createPortal } from "react-dom";
import { ErrorBoundary } from "react-error-boundary";

function Modal({ isOpen, onClose, title, children }) {
  const modalRef = useRef(null);

  useEffect(() => {
    if (!isOpen) return;
    const onKey = (e) => e.key === "Escape" && onClose();   // ESC
    document.addEventListener("keydown", onKey);
    document.body.style.overflow = "hidden";                 // scroll lock
    modalRef.current?.focus();                               // auto-focus
    return () => {
      document.removeEventListener("keydown", onKey);
      document.body.style.overflow = "";
    };
  }, [isOpen, onClose]);

  if (!isOpen) return null;

  return createPortal(                                        // portal (2.8)
    <div className="modal-backdrop" onClick={onClose}>
      <div
        ref={modalRef} tabIndex={-1}
        role="dialog" aria-modal="true" aria-labelledby="modal-title"   // a11y (1.9)
        className="modal" onClick={e => e.stopPropagation()}
      >
        <h2 id="modal-title">{title}</h2>
        <ErrorBoundary fallback={<p>Modal kontentida xato</p>}>          {/* izolyatsiya 2.4-bob */}
          {children}
        </ErrorBoundary>
        <button onClick={onClose} aria-label="Yopish">×</button>
      </div>
    </div>,
    document.body
  );
}
//  Portal + ref + ESC + scroll lock + auto-focus + a11y + error boundary — production modal
//    Amalda — Radix UI / Headless UI (tayyor, to'liq a11y — 2.14)

Misol 15 — Focus trap (fokusni modal ichida ushlab turish — 2.14, a11y)

Auto-focus (Misol 8) modal ochilganda fokusni modal'ga qo'yadi, lekin Tab bosilganda fokus modal'dan chiqib ketishi mumkin (orqadagi sahifaga) — ekran o'quvchi va klaviatura foydalanuvchisi uchun buzuq. Focus trapTab/Shift+Tab fokusni modal ichidagi birinchi va oxirgi elementlar orasida aylantiradi (tashqariga chiqarmaydi).

jsx
function useFocusTrap(ref, isActive) {
  useEffect(() => {
    if (!isActive || !ref.current) return;
    const modal = ref.current;

    // Modal ichidagi fokuslanadigan barcha elementlar:
    const selector =
      'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])';

    const onKey = (e) => {
      if (e.key !== "Tab") return;
      const items = modal.querySelectorAll(selector);
      if (items.length === 0) return;
      const first = items[0];
      const last = items[items.length - 1];

      if (e.shiftKey && document.activeElement === first) {
        e.preventDefault();
        last.focus();                 // boshidan orqaga  oxiriga o'ral
      } else if (!e.shiftKey && document.activeElement === last) {
        e.preventDefault();
        first.focus();                // oxiridan oldinga  boshiga o'ral
      }
    };

    modal.addEventListener("keydown", onKey);
    return () => modal.removeEventListener("keydown", onKey);   // cleanup (11.5)
  }, [ref, isActive]);
}

function Modal({ isOpen, onClose, children }) {
  const modalRef = useRef(null);
  useFocusTrap(modalRef, isOpen);       //  fokus modal ichida qamaladi

  useEffect(() => {
    if (isOpen) modalRef.current?.focus();   // ochilganda modal'ga fokus
  }, [isOpen]);

  if (!isOpen) return null;
  return createPortal(
    <div className="modal-backdrop" onClick={onClose}>
      <div ref={modalRef} tabIndex={-1} role="dialog" aria-modal="true"
           className="modal" onClick={e => e.stopPropagation()}>
        {children}
      </div>
    </div>,
    document.body
  );
}
//  Tab modal ichida aylanadi (tashqariga chiqmaydi) — a11y talabining muhim qismi (2.14)
//    To'liq a11y (yopilganda oldingi elementga fokus qaytarish ham) — Radix/Headless tayyor beradi

5. To'g'ri va noto'g'ri holatlar

1) Xato izolyatsiyasi

text
 error boundary yo'q (bitta xato  butun ilova oq ekran — 2.2)
 ildiz + sahifa + muhim widget error boundary (granular — 2.4)

2) Event/async xato

text
 error boundary handler/async xatosini ushlaydi deb kutish (2.5)
 handler/async — qo'lda try/catch + state (Misol 6)

3) Modal joylashuvi

text
 modal oddiy <div> (overflow/z-index'da kesiladi/qoladi — 2.7)
 createPortal(modal, document.body) (Misol 7)

4) Custom komponentga ref

text
 <MyInput ref={ref}/> (forwardRef/ref prop'siz — ishlamaydi — 2.10)
 forwardRef (React 18) yoki ref prop (React 19 — Misol 10, 11)

5) Modal a11y

text
 role/aria/focus yo'q (ekran o'quvchi/klaviatura ishlamaydi)
 role="dialog" aria-modal focus trap ESC (Misol 8, 14, 1.9)

6) useImperativeHandle ortiqcha

text
 har komponentga useImperativeHandle (imperativ — deklarativ afzal)
 faqat imperativ tabiatli (video/forma/animatsiya — 2.12)

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — Bitta komponent xatosi butun ilovani oq qiladi

Sababi: error boundary yo'q 2.2-bob. Yechimi: ildiz + muhim bo'laklarga error boundary (yoki react-error-boundary — Misol 1, 4).

Xato 2 — Error boundary qo'ydim, lekin xato ushlanmadi

Sababi: xato event handler yoki async kodda (error boundary ushlamaydi — 2.5). Yechimi: handler/async'da try/catch + state (Misol 6).

Xato 3 — Modal overflow: hidden ota ichida kesiladi

Sababi: portal ishlatilmagan (DOM ierarxiyasida qamalgan — 2.7). Yechimi: createPortal(modal, document.body) (Misol 7).

Xato 4 — Function components cannot be given refs ogohlantirishi

Sababi: custom komponentga ref berildi, lekin forwardRef/ref prop yo'q 2.10-bob. Yechimi: forwardRef (React 18) yoki ref prop (React 19 — Misol 10, 11).

Xato 5 — Modal ichiga bosganda ham yopiladi

Sababi: modal ichida stopPropagation yo'q (backdrop click bubbling — 2.8). Yechimi: modal <div onClick={e => e.stopPropagation()}> (Misol 7).

Xato 6 — Portal modal ichidagi tugma ota handler'ini ishga tushiradi

Sababi: portal event React daraxti bo'yicha bubbling 2.9-bob. Yechimi: stopPropagation, yoki ota handler mantig'ini tekshir (kutilgan xulq — 2.9).

Xato 7 — ref.current null (DOM hali yo'q)

Sababi: render paytida ref o'qildi (DOM render'dan keyin to'ladi — 11.5: 2.12). Yechimi: useEffect ichida yoki hodisa handlerida; ref.current?.focus().


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • Lazy/Suspense 11.8-bob: error boundary lazy yuklanish xatosini ushlaydi (Misol 5).
  • useRef (11.5: 2.12): forwardRef/useImperativeHandle — ref'ning ilg'or kengaytmasi.
  • Monitoring 10.9-bob: componentDidCatch/onError — Sentry'ga xato yuborish.
  • a11y 1.9-bob: modal role/aria/focus trap — ekran o'quvchi uchun.
  • Forma 11.10-bob: useImperativeHandle — custom input focus/reset/validate.
  • Performance 11.11-bob: callback ref — o'lchash, observer (virtualizatsiya).
  • UI kutubxonalar 12.6-bob: Radix/Headless — portal + ref + a11y tayyor.
  • Reliability 9.6-bob: granular error boundary — fault-tolerant UI (backend tamoyili frontend'da).

8. Eng yaxshi amaliyotlar (best practices)

  • Error boundary granular (ildiz + sahifa + muhim widget — kam zarar — 2.4).
  • react-error-boundary (qo'lda class o'rniga — reset/hook qulay — 2.6).
  • Event/async xato qo'lda (error boundary ushlamaydi — try/catch + state — 2.5).
  • Modal/overlay portal bilan (overflow/z-index ozod — 2.7).
  • Modal a11y to'liq (role=dialog, aria-modal, focus trap, ESC, scroll lock — 2.14, 1.9).
  • forwardRef/ref prop custom komponentga ref kerak bo'lsa (2.10, 2.11).
  • useImperativeHandle kam (faqat imperativ — video/forma; deklarativ afzal — 2.12).
  • Callback ref o'lchashga (element ulanganda darrov — observer — 2.13).
  • Xatoni logla (componentDidCatch/onError Sentry — monitoring — 10.9).
  • Tayyor kutubxona ko'rib chiq (Radix/Headless — modal/dropdown a11y — 2.14).

9. Amaliy loyiha: "Modal va Bildirishnoma Tizimi (UI Kutubxona)"

Portal, ref, error boundary'ni birlashtirib qayta ishlatiladigan UI tizimi qurish.

Maqsad

Professional modal, tooltip va toast (bildirishnoma) tizimini noldan yaratish — portal, ref, error boundary va a11y bilan.

Talablar (requirements)

  1. Modal: portal + backdrop + ESC + scroll lock + auto-focus + a11y (Misol 8, 14, 2.14).
  2. Error boundary: ildiz + modal kontenti alohida (granular — Misol 3, 2.4); react-error-boundary (Misol 4).
  3. Tooltip: portal bilan, joylashuv o'lchash (callback ref — Misol 9, 13).
  4. Toast: portal'ga render, avtomatik yo'qolish (taymer + cleanup — 11.5).
  5. forwardRef/ref prop: custom Input/Button — ref'ni uzatish (Misol 10, 11).
  6. useImperativeHandle: kamida bitta komponent maxsus API ochsin (masalan modal .open()/.close() — Misol 12).
  7. Event bubbling: portal modal ichidagi hodisa to'g'ri ishlasin (stopPropagation — 2.9).
  8. a11y: modal/tooltip — role, aria, klaviatura (2.14, 1.9).
  9. Reset: error boundary'da "qayta urinish" (Misol 2, 2.6).
  10. Qayta ishlatish: bu komponentlarni components/ui/da, butun ilovada ishlatib ko'r.

Maslahatlar (hint)

  • Modal'ni #modal-root yoki document.bodyga portal qil (overflow ota ichida sina — Misol 7).
  • ESC/scroll lock/focus — useEffect + cleanup (modal yopilganda tozalansin — Misol 8).
  • Modal ichiga bosish yopmasin — stopPropagation (Misol 7, Xato 5).
  • forwardRef (React 18) yoki ref prop (React 19) — versiyangizga qarab (Misol 10, 11).
  • Error boundary event/async xatoni ushlamaydi — toast uchun qo'lda try/catch 2.5-bob.
  • Amalda Radix/Headless bor — lekin avval o'zingiz yozib mexanizmni tushun 2.14-bob.

"Tayyor" mezonlari (acceptance criteria)

  • Modal portal bilan (overflow ota ichida to'liq ko'rinadi).
  • Modal: ESC, scroll lock, auto-focus, backdrop click ishlaydi.
  • Modal a11y (role=dialog, aria-modal, focus).
  • Tooltip portal + joylashuv o'lchash.
  • Toast portal + avtomatik yo'qolish (taymer cleanup).
  • Error boundary granular + reset.
  • Custom Input/Button ref uzatadi (forwardRef/ref prop).
  • Kamida bitta useImperativeHandle (maxsus API).
  • Komponentlar qayta ishlatiladigan (ui/ papkada).

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda React'ning ilg'or imkoniyatlarini chuqur o'rgandik:

  • Error boundary (nima — 2.2, qanday — 2.3, qayerga — 2.4, nimani ushlamaydi — 2.5, kutubxona — 2.6).
  • Portals (DOM teleport — 2.7, createPortal — 2.8, event bubbling React tree — 2.9).
  • Refs (forwardRef — 2.10, React 19 ref prop — 2.11, useImperativeHandle — 2.12, callback ref — 2.13); professional modal birlashmasi 2.14-bob.

Endi siz ishonchli (error boundary), modal/overlay (portal) va imperativ (ref) komponentlarni — ya'ni kutubxona darajasidagi professional UI'ni — qura olasiz. Bu vositalar oddiy ilovada kam ko'rinadi, lekin jiddiy loyihaning sifatini belgilaydi.

Keyingi bob — 11.13-bob: React pattern'lar — HOC, render props, compound components. Komponentlar orasida mantiq va UI'ni qayta ishlatishning klassik naqshlarini ko'ramiz: HOC (Higher-Order Component — komponentni o'rovchi funksiya), render props (funksiya orqali mantiq ulashish), va compound components (Tab, Accordion, Select kabi — bir-biriga bog'liq komponentlar oilasi, Context bilan). Bu naqshlar — custom hooklar 11.7-bob bilan solishtiriladi (qachon qaysi biri), va UI kutubxonalar (Radix, MUI) qanday qurilganini tushunish uchun zarur.


Foydalanilgan rasmiy/ishonchli manbalar

  • React rasmiy hujjati (react.dev) — "Catching rendering errors with an error boundary", Component (getDerivedStateFromError, componentDidCatch), createPortal, forwardRef, useImperativeHandle, useRef, "Manipulating the DOM with Refs", "Referencing Values with Refs"
  • React rasmiy hujjati (react.dev) — React 19 yangiliklari ("ref as a prop", forwardRef deprecatsiyasi), "Choosing the State Structure" (state va ref farqi)
  • react-error-boundary kutubxonasi rasmiy hujjati (npm) — ErrorBoundary, FallbackComponent, useErrorBoundary, resetErrorBoundary
  • WAI-ARIA Authoring Practices Guide (APG) — Dialog (Modal) Pattern (role="dialog", aria-modal, focus management va focus trap talablari)
  • Sentry rasmiy hujjati — React error boundary integratsiyasi (componentDidCatch/onError orqali xatoni monitoringga yuborish — 10.9 bilan bog'liq)
  • Radix UI Primitives va Headless UI rasmiy hujjatlari — portalga asoslangan, to'liq a11y'li komponentlar (Dialog/Modal, Tooltip, Popover/Dropdown)

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
11.12-bob: Error boundaries, portals, refs (ilg'or) — Wisar