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/useImperativeHandletalab qiladi. - Senior daraja — bu vositalar — murakkab, professional komponent dizaynining belgisi.
2. Nazariya — chuqur tushuntirish
2.1. Nega ilg'or vositalar — qanday muammolar
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 uchunNega 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. Yechim — Error boundary (xatoni ushlab, faqat o'sha qismni zaxira UI bilan almashtiradi). Muammo 2: modal/tooltip/dropdown DOM ierarxiyasida "qamalgan" — masalanoverflow: hiddenli konteyner ichidagi modal kesiladi, yokiz-indexota element bilan cheklangan (modal boshqa narsalar ortida qoladi). Yechim — Portal (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. Yechim — refs (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
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'ralganWidgetxato bersa, faqat o'sha joyda fallback ko'rinadi,Header/Footernormal ishlaydi. Tushunchani soddalashtirish: error boundary — butry/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
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).rendermetodidahasErrorbo'lsa fallback, aks holdachildren(odatdagi UI) qaytariladi. Ishlatish:<ErrorBoundary><Widget /></ErrorBoundary>. Ikki nuqta: (1) ikki metod ikki vazifa —getDerivedStateFromErrorUI'ni almashtiradi (render),componentDidCatchxatoni loglaydi (side effect, monitoring); (2) error boundary — React'ning yagona hali class talab qiladigan narsasi (yoki uni qo'lda yozish o'rnigareact-error-boundarykutubxonasini ishlatasiz — funksional interfeys — 2.6). Bir marta yozib, butun loyihada qayta ishlatasiz.
2.4. Error boundary qayerga joylashtirish (granularity)
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!)
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.onerrorError 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 ichidatry/catch(yoki async uchuntry/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 globalwindow.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
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 —errorvaresetErrorBoundaryprops'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)useErrorBoundaryhook — async/event handler xatosini ham boundary'ga "uzatish" (2.5 cheklovini qisman aylanib o'tish); (3) funksional, qulay, sinalgan. Ikki nuqta: (1) amaldareact-error-boundaryishlatiladi (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
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:
Modalkomponenti 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 elementoverflow: hiddenichida bo'lsa, u kesiladi (ko'rinmaydi); agar otaz-indexichida bo'lsa, modal boshqa narsalar ortida qoladi; portal modal'nibodyning 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
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 — odatdadocument.bodyyoki maxsus#modal-root). Modal misolida:createPortal(<div className="modal-backdrop">...</div>, document.body)— modal'ning butun JSX'ibodyga 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 komponentdae.stopPropagation()— modal ichiga bosganda backdrop'ningonClose'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)createPortal—react-dom'dan import qilinadi (React'dan emas); (2) konteyner sifatidadocument.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
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 kerakPortal 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 ubodyda (Parent'dan tashqarida), lekin React event bubbling JSX strukturasini kuzatadi — shuning uchunParent.onClickishlaydi (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'daonClickbo'lsa) — shuning uchun buni bilib turish va kerak bo'lsastopPropagationbilan boshqarish kerak. Bu tushuncha portal bilan murakkab UI (modal ichida forma, ichma-ich overlay) qurganda muhim.
2.10. useRef takror va forwardRef — ref uzatish
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"
useReftakror vaforwardRef— 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, chunkirefMyInputfunksiyasiga 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} /> })— endirefhaqiqiy<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 —forwardRefko'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).
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).ref— render'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/setIntervalid'si, oldingi qiymatni eslab qolish, tashqi kutilmagan sanoq) — ref. Klassik xato — sanoqchiniuseRefbilan yuritib, ekranda yangilanishini kutish:count.current++render qilmaydi, shuning uchun ekran o'zgarmaydi (bundauseStatekerak). Aksincha,setIntervalid'siniuseStateda saqlash — har taymer id o'zgarishi keraksiz render keltiradi (bundauseRefto'g'ri). Bir jumlada: state — UI'da ko'rinadigan (render qiluvchi) ma'lumot; ref — ko'rinmaydigan, o'zgaruvchan (render qilmaydigan) qiymat. Bu farqni tushunishforwardRef/useImperativeHandleni to'g'ri ishlatishning asosidir (ref imperativ tutqich, deklarativ state emas).
2.11. React 19 — ref as prop
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
forwardRefkerak edi (komponentni o'rab, ikkinchi argument sifatidarefolib). React 19'darefoddiy 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 (forwardRefbilan) 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 kutubxonalarforwardRef, yangilari ref prop). React'ning umumiy yo'nalishi — soddalik tomon (hooklar bilan boshlangan jarayon davom etadi).
2.12. useImperativeHandle — imperativ API ochish
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 komponentref.currentorqali faqat shu uch metodni ko'radi (ichkivideoRefyoki butun DOM elementni emas). MisoldaVideoPlayero'z ichki<video>elementini yashiradi va faqatplay/pause/resetmetodlarini ochadi — otaplayerRef.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
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 ref —
refga obyekt (useRef) o'rniga funksiya berish:<div ref={(node) => {...}} />— React bu funksiyani element DOM'ga ulanganda (node— element) va uzilganda (node—null) chaqiradi. Qachon callback ref kerak (useRefo'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 (useReffaqat.currentni passiv saqlaydi); (2) o'lchash yoki observer ulash uchun callback refuseEffect'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
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 yozingBirlashtirish — 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 —keydownlistener + 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 —bodyoverflow 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
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
// 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)
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)
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)
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 trap — Tab/Shift+Tab fokusni modal ichidagi birinchi va oxirgi elementlar orasida aylantiradi (tashqariga chiqarmaydi).
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 beradi5. To'g'ri va noto'g'ri holatlar
1) Xato izolyatsiyasi
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
error boundary handler/async xatosini ushlaydi deb kutish (2.5)
handler/async — qo'lda try/catch + state (Misol 6)3) Modal joylashuvi
modal oddiy <div> (overflow/z-index'da kesiladi/qoladi — 2.7)
createPortal(modal, document.body) (Misol 7)4) Custom komponentga ref
<MyInput ref={ref}/> (forwardRef/ref prop'siz — ishlamaydi — 2.10)
forwardRef (React 18) yoki ref prop (React 19 — Misol 10, 11)5) Modal a11y
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
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)
- Modal: portal + backdrop + ESC + scroll lock + auto-focus + a11y (Misol 8, 14, 2.14).
- Error boundary: ildiz + modal kontenti alohida (granular — Misol 3, 2.4); react-error-boundary (Misol 4).
- Tooltip: portal bilan, joylashuv o'lchash (callback ref — Misol 9, 13).
- Toast: portal'ga render, avtomatik yo'qolish (taymer + cleanup — 11.5).
- forwardRef/ref prop: custom Input/Button — ref'ni uzatish (Misol 10, 11).
- useImperativeHandle: kamida bitta komponent maxsus API ochsin (masalan modal
.open()/.close()— Misol 12). - Event bubbling: portal modal ichidagi hodisa to'g'ri ishlasin (stopPropagation — 2.9).
- a11y: modal/tooltip — role, aria, klaviatura (2.14, 1.9).
- Reset: error boundary'da "qayta urinish" (Misol 2, 2.6).
- Qayta ishlatish: bu komponentlarni
components/ui/da, butun ilovada ishlatib ko'r.
Maslahatlar (hint)
- Modal'ni
#modal-rootyokidocument.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 ("
refas a prop",forwardRefdeprecatsiyasi), "Choosing the State Structure" (state va ref farqi) react-error-boundarykutubxonasi 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/onErrororqali 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!