React Portallaridagi hodisalar tunneli sirini oching. Barqaror veb-ilovalar uchun DOM tuzilmasi farq qilganda ham hodisalar React komponentlar daraxti bo'ylab qanday tarqalishini o'rganing.
React Portallarida Hodisalar Tunneli: Barqaror UIlar uchun Chuqur Hodisalarni Tarqatish
Doimiy rivojlanib borayotgan front-end dasturlash olamida React butun dunyodagi dasturchilarga murakkab va yuqori interaktiv foydalanuvchi interfeyslarini yaratish imkoniyatini berishda davom etmoqda. React'dagi kuchli xususiyat – Portallar bizga farzand elementlarni ota-ona komponent ierarxiyasidan tashqarida joylashgan DOM tuguniga render qilish imkonini beradi. Bu qobiliyat modallar, maslahatlar va bildirishnomalar kabi ota-ona uslublari, z-index cheklovlari yoki joylashuv muammolaridan xalos bo'lishi kerak bo'lgan UI elementlarini yaratish uchun bebahodir. Biroq, Tokiodan Torontogacha va San-Pauludan Sidneygacha bo'lgan dasturchilar Portallarni joriy qilish ko'pincha muhim bir savolni tug'dirishini aniqlaydilar: bunday ajratilgan usulda render qilingan komponentlar orqali hodisalar qanday tarqaladi?
Ushbu keng qamrovli qoʻllanma React Portallaridagi hodisalar tunnelining qiziqarli dunyosiga chuqur kirib boradi. Biz React'ning sintetik hodisalar tizimi qanday qilib komponentlaringiz an'anaviy Hujjat Ob'ekt Modeli (DOM) ierarxiyasiga qarshi chiqayotgandek ko'rinsa ham, barqaror va oldindan aytib bo'ladigan hodisalar tarqalishini sinchkovlik bilan ta'minlashini tushuntirib beramiz. Asosiy "tunnel" mexanizmini tushunib, siz kutilmagan hodisa xatti-harakatlariga duch kelmasdan Portallarni muammosiz birlashtirib, yanada chidamli va qo'llab-quvvatlanadigan ilovalar yaratish uchun tajribaga ega bo'lasiz. Bu bilim turli global auditoriyalar va qurilmalar bo'ylab barqaror va oldindan aytib bo'ladigan foydalanuvchi tajribasini taqdim etish uchun juda muhimdir.
React Portallarini Tushunish: Ajratilgan DOMga Ko'prik
Aslini olganda, React Portali farzand komponentni uni mantiqan render qilayotgan komponentning DOM ierarxiyasidan tashqarida yashovchi DOM tuguniga render qilish usulini taqdim etadi. Bunga ReactDOM.createPortal(child, container) yordamida erishiladi. child parametri har qanday render qilinadigan React farzandi (masalan, element, satr yoki fragment) va container DOM elementi bo'lib, odatda document.createElement() bilan yaratilgan va document.bodyga qo'shilgan yoki document.getElementById('some-global-root') kabi mavjud elementdir.
Portallardan foydalanishning asosiy sababi uslub va joylashuv cheklovlaridan kelib chiqadi. Farzand komponent to'g'ridan-to'g'ri ota-ona ichida render qilinganda, u ota-onaning overflow: hidden, z-index stacking kontekstlari va joylashuv cheklovlari kabi CSS xususiyatlarini meros qilib oladi. Ba'zi UI elementlari uchun bu muammoli bo'lishi mumkin.
Nima uchun React Portallaridan foydalanish kerak? Umumiy global foydalanish holatlari:
- Modallar va Dialoglar: Bular odatda barcha boshqa kontentdan yuqorida ko'rinishini ta'minlash uchun DOMning eng yuqori darajasida joylashishi kerak, bu esa ota-ona elementning `overflow: hidden` yoki `z-index` kabi CSS qoidalaridan ta'sirlanmaydi. Bu foydalanuvchi Berlinda, Bangalorda yoki Buenos-Ayresda bo'lishidan qat'i nazar, barqaror foydalanuvchi tajribasi uchun juda muhimdir.
- Maslahatlar va Popoverlar: Modallarga o'xshab, bular ko'pincha ota-onalarining kesish yoki joylashuv kontekstlaridan qochishi kerak, bu esa to'liq ko'rinishni va ko'rish maydoniga nisbatan to'g'ri joylashuvni ta'minlaydi. Ota-onasida `overflow: hidden` bo'lgani uchun kesilgan maslahatni tasavvur qiling – Portallar buni hal qiladi.
- Bildirishnomalar va Toastlar: Ilova bo'ylab xabarlar, ular komponentlar daraxtining qayerida ishga tushirilganidan qat'i nazar, doimiy ravishda paydo bo'lishi kerak. Ular butun dunyo bo'ylab foydalanuvchilarga, ko'pincha xalaqit bermaydigan tarzda, muhim fikr-mulohazalarni taqdim etadi.
- Kontekst Menulari: O'ng tugmachani bosish menyulari yoki sichqoncha ko'rsatkichiga nisbatan render qilinishi va ajdod cheklovlaridan qochishi kerak bo'lgan maxsus kontekst menyulari, barcha foydalanuvchilar uchun tabiiy o'zaro ta'sir oqimini saqlab qoladi.
Oddiy misolni ko'rib chiqing:
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>React Portal Example</title>
</head>
<body>
<div id="root"></div>
<div id="modal-root"></div> <!-- Bu bizning Portal nishonimiz -->
<script src="index.js"></script>
</body>
</html>
// App.js (tushunarlilik uchun soddalashtirilgan)
import React from 'react';
import ReactDOM from 'react-dom';
function App() {
const [showModal, setShowModal] = React.useState(false);
return (
<div style={{ border: '2px solid red', padding: '20px' }}>
<h1>Asosiy Ilova Kontenti</h1>
<p>Bu kontent #root div ichida joylashgan.</p>
<button onClick={() => setShowModal(true)}>Modalni ko'rsatish</button>
{showModal && <Modal onClose={() => setShowModal(false)} />}
</div>
);
}
function Modal({ onClose }) {
return ReactDOM.createPortal(
<div style={{
position: 'fixed',
top: 0, left: 0, right: 0, bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
display: 'flex', alignItems: 'center', justifyContent: 'center'
}}>
<div style={{ backgroundColor: 'white', padding: '30px', borderRadius: '8px' }}>
<h2>Portaldan salom!</h2>
<p>Bu kontent '#root' ichida emas, '#modal-root' ichida render qilinadi.</p>
<button onClick={onClose}>Modalni yopish</button>
</div>
</div>,
document.getElementById('modal-root') // Ikkinchi argument: nishon DOM tuguni
);
}
ReactDOM.render(<App />, document.getElementById('root'));
Bu misolda, Modal komponenti React komponent daraxtida mantiqan Appning farzandi hisoblanadi. Biroq, uning DOM elementlari index.htmldagi #modal-root div ichida render qilinadi, bu esa App va uning avlodlari (masalan, "Modalni ko'rsatish" tugmasi) joylashgan #root divdan butunlay alohida. Bu tuzilmaviy mustaqillik uning kuchining kalitidir.
React'ning Hodisalar Tizimi: Sintetik Hodisalar va Delegatsiya haqida Qisqacha Eslatma
Portallar xususiyatlariga chuqur kirishdan oldin, React hodisalarni qanday boshqarishini yaxshi tushunish muhimdir. Mahalliy brauzer hodisalari tinglovchilarini to'g'ridan-to'g'ri biriktirish o'rniga, React bir nechta sabablarga ko'ra murakkab sintetik hodisalar tizimidan foydalanadi:
- Brauzerlararo Moslik: Mahalliy brauzer hodisalari turli brauzerlarda har xil ishlashi mumkin, bu nomuvofiqliklarga olib keladi. React'ning SyntheticEvent ob'ektlari mahalliy brauzer hodisalarini o'rab oladi, bu esa barcha qo'llab-quvvatlanadigan brauzerlarda normallashtirilgan, izchil interfeys va xatti-harakatni ta'minlaydi, bu sizning ilovangiz Nyu-Yorkdagi qurilmadan Nyu-Dehligacha bo'lgan qurilmalarda oldindan aytib bo'ladigan tarzda ishlashini ta'minlaydi.
- Samaradorlik va Xotira Tejamkorligi (Hodisalarni Delegatsiya qilish): React har bir DOM elementiga hodisa tinglovchisini biriktirmaydi. Buning o'rniga, u odatda ilovangizning ildiziga (masalan, `document` ob'ekti yoki asosiy React konteyneri) bitta (yoki bir nechta) hodisa tinglovchisini biriktiradi. Mahalliy hodisa DOM daraxti bo'ylab ushbu ildizga ko'tarilganda, React'ning delegatsiya qilingan tinglovchisi uni ushlab oladi. Hodisalarni delegatsiya qilish deb nomlanuvchi bu usul xotira sarfini sezilarli darajada kamaytiradi va samaradorlikni oshiradi, ayniqsa ko'plab interaktiv elementlarga ega yoki dinamik ravishda qo'shiladigan/o'chiriladigan komponentlarga ega ilovalarda.
- Hodisalarni Hovuzlash: SyntheticEvent ob'ektlari samaradorlik uchun hovuzlanadi va qayta ishlatiladi. Bu SyntheticEvent ob'ektining xususiyatlari faqat hodisaga ishlov beruvchining bajarilishi paytida haqiqiy ekanligini anglatadi. Agar siz hodisa xususiyatlarini asinxron ravishda saqlab qolishingiz kerak bo'lsa, `e.persist()` ni chaqirishingiz yoki kerakli xususiyatlarni chiqarib olishingiz kerak.
Hodisa Fazalari: Ushlab olish (Tunneling) va Ko'tarilish (Bubbling)
Brauzer hodisalari va shuning uchun React'ning sintetik hodisalari ikkita asosiy fazadan o'tadi:
- Ushlab olish Faza (yoki Tunneling Faza): Hodisa oynadan boshlanadi, DOM daraxti (yoki React komponent daraxti) bo'ylab nishon elementga tushadi. Mahalliy DOM API'larida `useCapture: true` bilan yoki React'ning maxsus `onClickCapture`, `onMouseDownCapture` va hk. bilan ro'yxatdan o'tgan tinglovchilar ushbu faza davomida ishga tushiriladi. Bu faza ajdod elementlariga hodisani nishonga yetib borguncha to'xtatib qolish imkonini beradi.
- Ko'tarilish Faza (Bubbling Phase): Nishon elementga yetib borgandan so'ng, hodisa nishon elementdan oynaga qaytib ko'tariladi. Ko'pgina standart hodisa tinglovchilari (React'ning `onClick`, `onMouseDown` kabi) ushbu faza davomida ishga tushiriladi, bu ota-ona elementlariga o'z farzandlaridan kelib chiqadigan hodisalarga javob berish imkonini beradi.
Hodisa Tarqalishini Boshqarish:
-
e.stopPropagation(): Bu usul React'ning sintetik hodisalar tizimida hodisaning ushlab olish va ko'tarilish fazalarida yanada tarqalishini oldini oladi. Asl DOMda, u joriy hodisaning DOM daraxti bo'ylab yuqoriga (ko'tarilish) yoki pastga (ushlab olish) tarqalishini oldini oladi. Bu kuchli vosita, lekin uni oqilona ishlatish kerak. -
e.preventDefault(): Bu usul hodisa bilan bog'liq standart harakatni to'xtatadi (masalan, formaning yuborilishini, havolaning navigatsiyasini yoki katakchaning belgilanishini oldini olish). Biroq, u hodisaning tarqalishini to'xtatmaydi.
Portal "Paradoksi": DOM va React Daraxti
Portallar va hodisalar bilan ishlashda tushunish kerak bo'lgan asosiy tushuncha bu React komponent daraxti (mantiqiy ierarxiya) va DOM ierarxiyasi (jismoniy tuzilma) o'rtasidagi fundamental farqdir. React komponentlarining aksariyati uchun bu ikki ierarxiya mukammal tarzda mos keladi. React'da aniqlangan farzand komponent o'zining mos keladigan DOM elementlarini ham ota-onasining DOM elementlarining farzandlari sifatida render qiladi.
Portallar bilan bu uyg'un muvofiqlik buziladi:
- Mantiqiy Ierarxiya (React Daraxti): Portal orqali render qilingan komponent hali ham uni render qilgan komponentning farzandi hisoblanadi. Bu mantiqiy ota-ona-farzand munosabati kontekstni tarqatish, holatni boshqarish (masalan, `useState`, `useReducer`) va eng muhimi, React'ning sintetik hodisalar tizimini qanday boshqarishi uchun juda muhimdir.
- Jismoniy Ierarxiya (DOM Daraxti): Portal tomonidan yaratilgan DOM elementlari DOM daraxtining butunlay boshqa qismida mavjud. Ular o'zlarining mantiqiy ota-onalarining DOM elementlariga aka-uka yoki hatto uzoq qarindosh bo'lib, ehtimol ularning asl render qilingan joyidan ancha uzoqda bo'lishi mumkin.
Bu ajratish ham Portallarning ulkan kuchining (ilgari qiyin bo'lgan UI joylashuvlarini amalga oshirish imkonini beradi) ham hodisalarni qayta ishlash bilan bog'liq dastlabki chalkashliklarning manbai hisoblanadi. Agar DOM tuzilmasi boshqacha bo'lsa, hodisalar qanday qilib uning jismoniy DOM ajdodi bo'lmagan mantiqiy ota-onaga tarqalishi mumkin?
Portallar bilan Hodisalarni Tarqatish: "Tunnel" Mexanizmi Tushuntirildi
Mana shu yerda React'ning sintetik hodisalar tizimining nafisligi va uzoqni ko'ra bilishi haqiqatan ham namoyon bo'ladi. React Portal ichida render qilingan komponentlardan keladigan hodisalar, DOMdagi jismoniy pozitsiyasidan qat'i nazar, React komponent daraxti orqali tarqalishini ta'minlaydi va mantiqiy ierarxiyani saqlab qoladi. Bu ajoyib jarayonni biz "Hodisa Tunneli" deb ataymiz.
Portal ichidagi tugmadan kelib chiqadigan hodisani tasavvur qiling. Mana hodisalar ketma-ketligi, konseptual ravishda:
-
Mahalliy DOM Hodisasi Ishga Tushadi: Bosish birinchi navbatda uning haqiqiy DOM joylashuvidagi (masalan,
#modal-rootdiv ichida) tugmada mahalliy brauzer hodisasini ishga tushiradi. -
Mahalliy Hodisa Hujjat Ildiziga Ko'tariladi: Bu mahalliy hodisa keyin haqiqiy DOM ierarxiyasi bo'ylab (tugmadan,
#modal-rootorqali, `document.body`ga va nihoyat `document` ildizining o'ziga) ko'tariladi. Bu standart brauzer xatti-harakatidir. - React'ning Delegatsiya Qilingan Tinglovchisi Ushlaydi: React'ning delegatsiya qilingan hodisa tinglovchisi (odatda `document` darajasida biriktirilgan) bu mahalliy hodisani ushlab oladi.
- React Sintetik Hodisani Yuboradi - Mantiqiy Ushlab olish/Tunneling Faza: Hodisani jismoniy DOM nishonida darhol qayta ishlash o'rniga, React'ning hodisalar tizimi birinchi navbatda *React ilovasining ildizidan Portalni render qilgan komponentgacha* bo'lgan mantiqiy yo'lni aniqlaydi. So'ngra u ushbu mantiqiy daraxtdagi barcha oraliq React komponentlari orqali ushlab olish fazasini (pastga tunnel) simulyatsiya qiladi. Bu ularning mos keladigan DOM elementlari Portalning jismoniy DOM joylashuvining to'g'ridan-to'g'ri ajdodlari bo'lmasa ham sodir bo'ladi. Ushbu mantiqiy ajdodlardagi har qanday `onClickCapture` yoki shunga o'xshash ushlab oluvchi ishlov beruvchilar kutilgan tartibda ishga tushadi. Buni jismoniy kabellar qayerda yotqizilganidan qat'i nazar, oldindan belgilangan mantiqiy tarmoq yo'li orqali yuborilayotgan xabar deb o'ylang.
- Nishon Hodisa Ishlov Beruvchisi Bajariladi: Hodisa Portal ichidagi asl nishon komponentiga yetib boradi va uning maxsus ishlov beruvchisi (masalan, tugmadagi `onClick`) bajariladi.
- React Sintetik Hodisani Yuboradi - Mantiqiy Ko'tarilish Faza: Nishon ishlov beruvchisidan so'ng, hodisa mantiqiy React komponent daraxti bo'ylab, Portal ichida render qilingan komponentdan, Portalning ota-onasi orqali va React ilovasining ildiziga qadar yuqoriga tarqaladi. Ushbu mantiqiy ajdodlardagi `onClick` kabi standart ko'tarilish tinglovchilari ishga tushadi.
Mohiyatan, React'ning hodisalar tizimi o'zining sintetik hodisalari uchun jismoniy DOM nomuvofiqliklarini ajoyib tarzda abstraktlashtiradi. U Portalni go'yo uning farzandlari hodisalarni tarqatish maqsadlarida to'g'ridan-to'g'ri ota-onaning DOM pastki daraxtida render qilingandek qabul qiladi. Hodisa mantiqiy React ierarxiyasi orqali "tunnel" hosil qiladi, bu esa ushbu mexanizm tushunilgandan so'ng Portallar bilan hodisalarni boshqarishni ajablanarli darajada intuitiv qiladi.
Tunnelga oid Ko'rgazmali Misol:
Keling, hodisalar oqimini kuzatish uchun oldingi misolimizni aniqroq loglar bilan qayta ko'rib chiqaylik:
// App.js
import React from 'react';
import ReactDOM from 'react-dom';
function App() {
const [showModal, setShowModal] = React.useState(false);
// Bu ishlov beruvchilar Modalning mantiqiy ota-onasida
const handleAppDivClickCapture = () => console.log('1. App div bosildi (CAPTURE)!');
const handleAppDivClick = () => console.log('5. App div bosildi (BUBBLE)!');
return (
<div style={{ border: '2px solid red', padding: '20px' }}
onClickCapture={handleAppDivClickCapture} <!-- Pastga tunnel paytida ishga tushadi -->
onClick={handleAppDivClick}> <!-- Yuqoriga ko'tarilish paytida ishga tushadi -->
<h1>Asosiy Ilova</h1>
<button onClick={() => setShowModal(true)}>Modalni ko'rsatish</button>
{showModal && <Modal onClose={() => setShowModal(false)} />}
</div>
);
}
function Modal({ onClose }) {
const handleModalOverlayClickCapture = () => console.log('2. Modal qoplamasi bosildi (CAPTURE)!');
const handleModalOverlayClick = () => console.log('4. Modal qoplamasi bosildi (BUBBLE)!');
return ReactDOM.createPortal(
<div style={{
position: 'fixed', top: 0, left: 0, right: 0, bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
display: 'flex', alignItems: 'center', justifyContent: 'center'
}}
onClickCapture={handleModalOverlayClickCapture} <!-- Portalga tunnel paytida ishga tushadi -->
onClick={handleModalOverlayClick}>
<div style={{ backgroundColor: 'white', padding: '30px', borderRadius: '8px' }}>
<h2>Portaldan salom!</h2>
<p>Quyidagi tugmani bosing.</p>
<button onClick={() => { console.log('3. Modalni yopish tugmasi bosildi (TARGET)!'); onClose(); }}>Modalni yopish</button>
</div>
</div>,
document.getElementById('modal-root')
);
}
ReactDOM.render(<App />, document.getElementById('root'));
Agar siz "Modalni yopish" tugmasini bossangiz, kutilayotgan konsol chiqishi quyidagicha bo'ladi:
1. App div bosildi (CAPTURE)!(Hodisa mantiqiy ota-ona orqali pastga tunnel hosil qilganda ishga tushadi)2. Modal qoplamasi bosildi (CAPTURE)!(Hodisa Portal ildiziga pastga tunnel hosil qilganda ishga tushadi)3. Modalni yopish tugmasi bosildi (TARGET)!(Haqiqiy nishonning ishlov beruvchisi)4. Modal qoplamasi bosildi (BUBBLE)!(Hodisa Portal ildizidan yuqoriga ko'tarilganda ishga tushadi)5. App div bosildi (BUBBLE)!(Hodisa mantiqiy ota-onaga yuqoriga ko'tarilganda ishga tushadi)
Bu ketma-ketlik, "Modal qoplamasi" jismonan #modal-root'da va "App div" #root'da render qilingan bo'lsa-da, React'ning hodisalar tizimi ularni hodisalarni tarqatish maqsadlarida go'yo "Modal" "App"ning to'g'ridan-to'g'ri farzandi kabi o'zaro ta'sir qilishini aniq ko'rsatadi. Bu izchillik React'ning hodisalar modelining asosidir.
Hodisalarni Ushlab olishga Chuqur Kirish (Haqiqiy Tunnel Faza)
Ushlab olish fazasi Portal hodisalarining tarqalishini tushunish uchun ayniqsa dolzarb va kuchlidir. Portalda render qilingan elementda hodisa yuz berganda, React'ning sintetik hodisalar tizimi Portal kontentini hodisalar oqimi uchun go'yo uning mantiqiy ota-onasi ichida chuqur joylashgandek "ko'rsatadi". Shuning uchun, ushlab olish fazasi React komponent daraxti bo'ylab ildizdan, Portalning mantiqiy ota-onasi (createPortalni chaqirgan komponent) orqali va *keyin* Portal kontenti ichiga o'tadi.
Bu "pastga tunnel" jihati shuni anglatadiki, Portalning har qanday mantiqiy ajdodi hodisani Portal kontentiga yetib borguncha *to'xtatib qolishi* mumkin. Bu quyidagi kabi xususiyatlarni amalga oshirish uchun muhim qobiliyatdir:
- Global Qisqa Tugmalar/Yorliqlar: Yuqori tartibli komponent yoki `document` darajasidagi tinglovchi (React'ning `useEffect` va `onClickCapture` orqali) klaviatura hodisalari yoki bosishlarni chuqur joylashgan Portal tomonidan qayta ishlanmasdan oldin aniqlay oladi, bu esa global ilova nazoratini ta'minlaydi.
- Qoplamalarni Boshqarish: Portalni (mantiqan) o'rab turgan komponent `onClickCapture` yordamida uning mantiqiy maydonidan o'tadigan har qanday bosishni aniqlashi mumkin, Portalning jismoniy DOM joylashuvidan qat'i nazar, bu murakkab qoplamani yopish mantiqini amalga oshirish imkonini beradi.
- O'zaro Ta'sirni Oldini Olish: Kamdan-kam hollarda, ajdod vaqtinchalik UI qulfi yoki shartli o'zaro ta'sir qatlamining bir qismi sifatida hodisaning Portal kontentiga yetib borishini oldini olishi kerak bo'lishi mumkin.
`document.body` bosish ishlov beruvchisi va Portalning mantiqiy ota-onasidagi React `onClickCapture` ni taqqoslang:
// App.js
import React from 'react';
import ReactDOM from 'react-dom';
function App() {
const [showNotification, setShowNotification] = React.useState(false);
React.useEffect(() => {
// Mahalliy hujjat bosish tinglovchisi: jismoniy DOM ierarxiyasini hurmat qiladi
const handleNativeDocumentClick = () => {
console.log('--- NATIVE: Hujjat bosilishi aniqlandi. (DOM pozitsiyasiga qarab birinchi ishga tushadi) ---');
};
document.addEventListener('click', handleNativeDocumentClick);
return () => document.removeEventListener('click', handleNativeDocumentClick);
}, []);
const handleAppDivClickCapture = () => console.log('1. APP: CAPTURE hodisasi (React Synthetic - mantiqiy ota-ona)');
return (
<div onClickCapture={handleAppDivClickCapture}>
<h2>Asosiy Ilova</h2>
<button onClick={() => setShowNotification(true)}>Bildirishnomani ko'rsatish</button>
{showNotification && <Notification />}
</div>
);
}
function Notification() {
const handleNotificationDivClickCapture = () => console.log('2. NOTIFICATION: CAPTURE hodisasi (React Synthetic - Portal ildizi)');
return ReactDOM.createPortal(
<div style={{ border: '1px solid blue', padding: '10px' }}
onClickCapture={handleNotificationDivClickCapture}>
<p>Portaldan xabar.</p>
<button onClick={() => console.log('3. NOTIFICATION TUGMASI: Bosildi (TARGET)!')}>OK</button>
</div>,
document.getElementById('notification-root') // index.html'dagi boshqa ildiz, masalan, <div id="notification-root"></div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
Agar siz Notification Portali ichidagi "OK" tugmasini bossangiz, konsol chiqishi shunday ko'rinishi mumkin:
--- NATIVE: Hujjat bosilishi aniqlandi. (DOM pozitsiyasiga qarab birinchi ishga tushadi) ---(Bu `document.addEventListener`'dan ishga tushadi, u mahalliy DOMni hurmat qiladi, shuning uchun brauzer tomonidan birinchi qayta ishlanadi.)1. APP: CAPTURE hodisasi (React Synthetic - mantiqiy ota-ona)(React'ning sintetik hodisalar tizimi `App` komponentidan o'zining mantiqiy tunnel yo'lini boshlaydi.)2. NOTIFICATION: CAPTURE hodisasi (React Synthetic - Portal ildizi)(Tunnel Portal kontentining ildiziga davom etadi.)3. NOTIFICATION TUGMASI: Bosildi (TARGET)!(Nishon elementining `onClick` ishlov beruvchisi ishga tushadi.)- (Agar Notification div yoki App div'da ko'tariluvchi ishlov beruvchilar bo'lsa, ular keyingi teskari tartibda ishga tushardi.)
Bu ketma-ketlik React'ning hodisalar tizimi ham ushlab olish, ham ko'tarilish fazalari uchun mantiqiy komponent ierarxiyasini ustun qo'yishini, bu esa xom mahalliy DOM hodisalaridan farqli o'laroq, ilovangiz bo'ylab izchil hodisalar modelini taqdim etishini yaqqol ko'rsatadi. Bu o'zaro ta'sirni tushunish disk raskadrovka qilish va barqaror hodisalar oqimlarini loyihalash uchun juda muhimdir.
Amaliy Stsenariylar va Harakatga Keltiruvchi G'oyalar
1-Stsenariy: Modallar uchun Global Tashqarida Bosish Mantiqi
Modallar uchun umumiy talab, barcha madaniyatlar va mintaqalarda yaxshi foydalanuvchi tajribasi uchun muhim bo'lgan, foydalanuvchi modalning asosiy kontent maydonidan tashqarida biror joyni bosganda uni yopishdir. Portal hodisa tunnelini tushunmasdan, bu murakkab bo'lishi mumkin. Barqaror, "React-uslubidagi" usul hodisa tunnelidan va `stopPropagation()`'dan foydalanadi.
function AppWithModal() {
const [isOpen, setIsOpen] = React.useState(false);
const modalRef = React.useRef(null);
// Bu ishlov beruvchi App ichidagi *mantiqiy* har qanday bosish uchun ishga tushadi,
// shu jumladan Modaldan yuqoriga tunnel qilgan bosishlar, agar to'xtatilmasa.
const handleAppClick = () => {
console.log('App bosishni qabul qildi (BUBBLE).');
// Agar modal kontentidan tashqarida, lekin qoplamadagi bosish modalni yopishi kerak bo'lsa,
// va shu qoplamaning onClick ishlov beruvchisi modalni yopsa, unda bu App ishlov beruvchisi
// faqat hodisa qoplamadan o'tib ko'tarilganda yoki modal ochiq bo'lmaganda ishga tushishi mumkin.
};
const handleCloseModal = () => setIsOpen(false);
return (
<div onClick={handleAppClick}>
<h2>Ilova Kontenti</h2>
<button onClick={() => setIsOpen(true)}>Modalni ochish</button>
{isOpen && <ClickOutsideModal onClose={handleCloseModal} />}
</div>
);
}
function ClickOutsideModal({ onClose }) {
// Portalning bu tashqi divi yarim shaffof qoplama vazifasini bajaradi.
// Uning onClick ishlov beruvchisi modalni FAQAT bosish unga ko'tarilgan bo'lsa yopadi,
// ya'ni u ichki modal kontentidan kelib chiqmagan VA to'xtatilmagan bo'lsa.
return ReactDOM.createPortal(
<div style={{
position: 'fixed', top: 0, left: 0, right: 0, bottom: 0,
backgroundColor: 'rgba(0,0,0,0.6)',
display: 'flex', alignItems: 'center', justifyContent: 'center'
}}
onClick={onClose} > <!-- Bu ishlov beruvchi ichki kontentdan tashqarida bosilsa modalni yopadi -->
<div style={{
backgroundColor: 'white', padding: '25px', borderRadius: '10px',
minWidth: '300px', maxWidth: '80%'
}}
// Eng muhimi, bu yerda tarqalishni to'xtatish, bosishning qoplamaning onClick
// ishlov beruvchisiga va shuning uchun App'ning onClick ishlov beruvchisiga ko'tarilishini oldini oladi.
onClick={(e) => e.stopPropagation()} >
<h3>Menga yoki Tashqariga Bosing!</h3>
<p>Modalni yopish uchun bu oq qutidan tashqaridagi istalgan joyni bosing.</p>
<button onClick={onClose}>Tugma bilan yopish</button>
</div>
</div>,
document.getElementById('modal-root')
);
}
Ushbu barqaror misolda: foydalanuvchi oq modal kontent qutisi *ichida* bosganda, ichki `div`dagi `e.stopPropagation()` sintetik bosish hodisasining yarim shaffof qoplamaning `onClick={onClose}` ishlov beruvchisiga ko'tarilishini oldini oladi. React'ning tunnel hosil qilishi tufayli, u hodisaning `AppWithModal`ning `onClick={handleAppClick}`iga yanada ko'tarilishini ham oldini oladi. Agar foydalanuvchi oq kontent qutisidan *tashqarida*, lekin hali ham yarim shaffof qoplama *ustida* bossa, qoplamaning `onClick={onClose}` ishlov beruvchisi ishga tushadi va modalni yopadi. Bu naqsh foydalanuvchilarning malakasi yoki o'zaro ta'sir odatlaridan qat'i nazar, intuitiv xatti-harakatni ta'minlaydi.
2-Stsenariy: Ajdod Ishlov Beruvchilarining Portal Hodisalari uchun Ishga Tushishini Oldini Olish
Ba'zan sizda ajdod komponentda global hodisa tinglovchisi (masalan, log yozish, tahlil qilish yoki ilova bo'ylab klaviatura yorliqlari uchun) bo'ladi va siz Portal farzandidan kelib chiqadigan hodisalarning uni ishga tushirishini oldini olishni xohlaysiz. Aynan shu yerda Portal kontenti ichida `e.stopPropagation()`'dan oqilona foydalanish toza va oldindan aytib bo'ladigan hodisalar oqimi uchun muhim ahamiyatga ega bo'ladi.
function AnalyticsApp() {
const [showPanel, setShowPanel] = React.useState(false);
const handleGlobalClick = () => {
console.log('AnalyticsApp: Asosiy ilovaning istalgan joyida bosish aniqlandi (tahlil/log yozish uchun).');
};
return (
<div onClick={handleGlobalClick}> <!-- Bu unga ko'tarilgan barcha bosishlarni logga yozadi -->
<h2>Tahlil bilan Asosiy Ilova</h2>
<button onClick={() => setShowPanel(true)}>Harakat Panelini ochish</button>
{showPanel && <ActionPanel onClose={() => setShowPanel(false)} />}
</div>
);
}
function ActionPanel({ onClose }) {
// Bu Portal alohida DOM tuguniga render qilinadi (masalan, <div id="panel-root">).
// Biz bu panel *ichidagi* bosishlar AnalyticsApp'ning global ishlov beruvchisini ishga tushirmasligini xohlaymiz.
return ReactDOM.createPortal(
<div style={{ border: '1px solid darkgreen', padding: '15px', backgroundColor: '#f0f0f0' }}
onClick={(e) => e.stopPropagation()} > <!-- Mantiqiy tarqalishni to'xtatish uchun muhim -->
<h3>Harakatni Bajarish</h3>
<p>Bu o'zaro ta'sir izolyatsiya qilinishi kerak.</p>
<button onClick={() => { console.log('Harakat bajarildi!'); onClose(); }}>Yuborish</button>
<button onClick={onClose}>Bekor qilish</button>
</div>,
document.getElementById('panel-root')
);
}
`ActionPanel`'ning Portal kontentining eng tashqi `div`iga `onClick={(e) => e.stopPropagation()}` ni joylashtirish orqali panel ichida kelib chiqadigan har qanday sintetik bosish hodisasining tarqalishi shu nuqtada to'xtatiladi. U `AnalyticsApp`ning `handleGlobalClick`'iga tunnel orqali ko'tarilmaydi, shu bilan sizning tahlil yoki boshqa global ishlov beruvchilaringizni Portalga xos o'zaro ta'sirlardan toza saqlaydi. Bu sizning ilovangizda qaysi hodisalar qaysi mantiqiy harakatlarni ishga tushirishini aniq nazorat qilish imkonini beradi.
3-Stsenariy: Portallar bilan Kontekst API
Kontekst ma'lumotlarni har bir darajada prop'larni qo'lda uzatmasdan komponent daraxti orqali o'tkazishning kuchli usulini taqdim etadi. Umumiy tashvish - kontekst DOMdan ajratilganligini hisobga olgan holda Portallar bo'ylab ishlaydimi yoki yo'qmi. Yaxshi xabar shundaki, ha, ishlaydi! Portallar hali ham mantiqiy React komponent daraxtining bir qismi bo'lgani uchun, ular o'zlarining mantiqiy ajdodlari tomonidan taqdim etilgan kontekstni iste'mol qila oladilar, bu esa React'ning ichki mexanizmlari komponent daraxtiga ustunlik berishi g'oyasini mustahkamlaydi.
const ThemeContext = React.createContext('light');
function ThemedApp() {
const [theme, setTheme] = React.useState('light');
return (
<ThemeContext.Provider value={theme}>
<div style={{ padding: '20px', backgroundColor: theme === 'light' ? '#f8f8f8' : '#333', color: theme === 'light' ? '#333' : '#eee' }}>
<h2>Mavzuli Ilova ({theme} rejim)</h2>
<p>Bu ilova foydalanuvchi afzalliklariga moslashadi, bu global dizayn tamoyilidir.</p>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>Mavzuni o'zgartirish</button>
<ThemedPortalMessage />
</div>
</ThemeContext.Provider>
);
}
function ThemedPortalMessage() {
// Bu komponent, Portalda render qilinishiga qaramay, hali ham mantiqiy ota-onasidan kontekstni iste'mol qiladi.
const theme = React.useContext(ThemeContext);
return ReactDOM.createPortal(
<div style={{
position: 'fixed', top: '20px', right: '20px', padding: '15px', borderRadius: '5px',
backgroundColor: theme === 'light' ? 'lightblue' : 'darkblue',
color: 'white',
boxShadow: '0 2px 10px rgba(0,0,0,0.2)'
}}>
<p>Bu xabar mavzuli: <strong>{theme} rejimi</strong>.</p>
<small>Asosiy DOM daraxtidan tashqarida, lekin mantiqiy React konteksti ichida render qilingan.</small>
</div>,
document.getElementById('notification-root') // index.html'da <div id="notification-root"></div> mavjud deb taxmin qilinadi
);
}
ThemedPortalMessage #notification-root (alohida DOM tuguni) ichiga render qilinsa ham, u ThemedApp'dan `theme` kontekstini muvaffaqiyatli qabul qiladi. Bu kontekst tarqalishi mantiqiy React daraxtiga ergashishini ko'rsatadi, bu esa hodisalar tarqalishi qanday ishlashini aks ettiradi. Bu izchillik Portallardan foydalanadigan murakkab UI komponentlari uchun holatni boshqarishni soddalashtiradi.
4-Stsenariy: Ichma-ich Joylashgan Portallarda Hodisalarga Ishlov Berish (Murakkab)
Kamroq uchrasa-da, Portallarni ichma-ich joylashtirish mumkin, ya'ni Portalda render qilingan komponent o'zi boshqa Portalni render qiladi. Hodisa tunnel mexanizmi xuddi shu tamoyillarni kengaytirib, bu murakkab stsenariylarni osonlikcha boshqaradi:
- Hodisa eng chuqur Portal kontentidan kelib chiqadi.
- U shu eng chuqur Portal ichidagi React komponentlari orqali yuqoriga ko'tariladi.
- So'ngra u shu eng chuqur Portalni *render qilgan* komponentga tunnel orqali ko'tariladi.
- U yerdan u keyingi mantiqiy ota-onaga ko'tariladi, bu boshqa Portal kontenti bo'lishi mumkin.
- Bu butun React ilovasining ildiziga yetguncha davom etadi.
Asosiy xulosa shundaki, mantiqiy React komponent ierarxiyasi Portallar qancha DOM ajratish qatlamlarini kiritishidan qat'i nazar, hodisalar tarqalishi uchun yagona haqiqat manbai bo'lib qoladi. Bu oldindan aytib bo'lishlik yuqori darajada modulli va kengaytiriladigan UI tizimlarini yaratish uchun eng muhimdir.
Global Ilovalar uchun Eng Yaxshi Amaliyotlar va Mulohazalar
-
e.stopPropagation()'dan Oqilona Foydalanish: Kuchli bo'lishiga qaramay,stopPropagation()'ni haddan tashqari ko'p ishlatish mo'rt va disk raskadrovka qilish qiyin bo'lgan kodga olib kelishi mumkin. Uni faqat ma'lum hodisalarning mantiqiy daraxt bo'ylab yuqoriga tarqalishini oldini olish kerak bo'lgan joyda, odatda Portal kontentingizning ildizida uning o'zaro ta'sirini izolyatsiya qilish uchun aniq ishlating. Sizning aniq talabingizga qarab, manbada tarqalishni to'xtatish o'rniga ajdodda `onClickCapture` dan foydalanish to'xtatish uchun yaxshiroq yondashuv bo'lishi mumkinligini o'ylab ko'ring. -
Kirish Qulayligi (A11y) Eng Muhim: Portallar, ayniqsa modallar va dialoglar uchun, ko'pincha global, inklyuziv foydalanuvchilar bazasi uchun hal qilinishi kerak bo'lgan jiddiy kirish qulayligi muammolarini keltirib chiqaradi. Quyidagilarga ishonch hosil qiling:
- Fokusni Boshqarish: Portal (masalan, modal) ochilganda, fokus dasturiy ravishda uning ichiga ko'chirilishi va ushlab turilishi kerak. Klaviatura yoki yordamchi texnologiyalar bilan navigatsiya qilayotgan foydalanuvchilar buni kutishadi. Keyin u yopilganda fokus Portalni ochgan elementga qaytarilishi kerak. `react-focus-lock` yoki `focus-trap-react` kabi kutubxonalar bu murakkab xatti-harakatni brauzerlar va qurilmalar bo'ylab ishonchli boshqarish uchun juda tavsiya etiladi.
- Klaviatura Navigatsiyasi: Foydalanuvchilar Portal ichidagi barcha elementlar bilan faqat klaviatura yordamida o'zaro ta'sir o'tkaza olishiga ishonch hosil qiling (masalan, navigatsiya uchun Tab, Shift+Tab, modallarni yopish uchun Esc). Bu motor nuqsonlari bo'lgan yoki shunchaki klaviatura bilan ishlashni afzal ko'radigan foydalanuvchilar uchun asosiy talabdir.
- ARIA Rollari va Atributlari: Tegishli WAI-ARIA rollari va atributlaridan foydalaning. Masalan, modal odatda `role="dialog"` (yoki `alertdialog`), `aria-modal="true"` va uni sarlavhasi va tavsifiga bog'lash uchun `aria-labelledby` / `aria-describedby` ga ega bo'lishi kerak. Bu ekran o'quvchilari va boshqa yordamchi texnologiyalarga muhim semantik ma'lumotlarni taqdim etadi.
- `inert` Atributi: Zamonaviy brauzerlar uchun, faol modal/portaldan tashqaridagi elementlarda `inert` atributidan foydalanishni ko'rib chiqing, bu orqa fondagi kontent bilan fokus va o'zaro ta'sirni oldini oladi va yordamchi texnologiya foydalanuvchilari uchun foydalanuvchi tajribasini yaxshilaydi.
- Aylantirishni Qulflash: Modal yoki to'liq ekranli Portal ochilganda, siz ko'pincha orqa fondagi kontentning aylanishini oldini olishni xohlaysiz. Bu keng tarqalgan UX naqshidir va odatda `body` elementini `overflow: hidden` bilan uslublashni o'z ichiga oladi. Turli operatsion tizimlar va brauzerlarda yuzaga kelishi mumkin bo'lgan joylashuv o'zgarishlari yoki aylantirish paneli yo'qolishi muammolariga e'tibor bering, bu butun dunyo bo'ylab foydalanuvchilarga ta'sir qilishi mumkin. `body-scroll-lock` kabi kutubxonalar yordam berishi mumkin.
- Server Tomonidan Renderlash (SSR): Agar siz SSR'dan foydalanayotgan bo'lsangiz, Portal konteyner elementlaringiz (masalan, `#modal-root`) dastlabki HTML chiqishingizda mavjud bo'lishiga ishonch hosil qiling yoki ularning yaratilishini mijoz tomonida boshqaring, bu gidratatsiya nomuvofiqliklarini oldini olish va silliq dastlabki renderlashni ta'minlash uchun. Bu, ayniqsa internet aloqasi sekinroq bo'lgan mintaqalarda ishlash samaradorligi va SEO uchun juda muhimdir.
- Sinov Strategiyalari: Portallardan foydalanadigan komponentlarni sinovdan o'tkazayotganda, Portal kontenti boshqa DOM tugunida render qilinishini unutmang. `@testing-library/react` kabi vositalar odatda Portal kontentini uning qulaylik roli yoki matn kontenti orqali topish uchun etarlicha barqarordir, lekin ba'zida uning mavjudligi yoki o'zaro ta'sirini tasdiqlash uchun `document.body` yoki maxsus Portal konteynerini to'g'ridan-to'g'ri tekshirishingiz kerak bo'lishi mumkin. Foydalanuvchi o'zaro ta'sirini simulyatsiya qiladigan va kutilgan hodisalar oqimini tekshiradigan testlar yozing.
Umumiy Qiyinchiliklar va Nosozliklarni Bartaraf Etish
- DOM va React Ierarxiyasini Chalkashtirish: Takror aytilganidek, bu eng keng tarqalgan qiyinchilikdir. Har doim yodda tutingki, React'ning sintetik hodisalari uchun mantiqiy React komponent daraxti tarqalishni belgilaydi, jismoniy DOM tuzilmasi emas. Komponent daraxtingizni chizib olish ko'pincha buni aniqlashtirishga yordam beradi.
- Mahalliy Hodisa Tinglovchilari va React Sintetik Hodisalari: Mahalliy DOM hodisa tinglovchilarini (masalan, `document.addEventListener('click', handler)`) React'ning sintetik hodisalari bilan aralashtirishda juda ehtiyot bo'ling. Mahalliy tinglovchilar har doim jismoniy DOM ierarxiyasini hurmat qiladi, React'ning hodisalari esa mantiqiy React ierarxiyasini hurmat qiladi. Agar bu tushunilmasa, kutilmagan bajarilish tartibiga olib kelishi mumkin, bunda mahalliy ishlov beruvchi sintetik ishlov beruvchidan oldin ishga tushishi yoki aksincha, ular qayerga biriktirilganiga va hodisa fazasiga bog'liq.
- `stopPropagation()`'ga Haddan Tashqari Tayanish: Muayyan stsenariylarda zarur bo'lsa-da, `stopPropagation()`'ni haddan tashqari ko'p ishlatish hodisa mantiqingizni qattiq va qo'llab-quvvatlashni qiyinlashtirishi mumkin. Komponent o'zaro ta'sirini shunday loyihalashtirishga harakat qilingki, hodisalar majburiy to'xtatilmasdan tabiiy ravishda oqsin va `stopPropagation()`'ga faqat komponent xatti-harakatini izolyatsiya qilish uchun qat'iy zarur bo'lganda murojaat qiling.
- Hodisa Ishlov Beruvchilarini Disk Raskadrovka Qilish: Agar hodisa ishlov beruvchisi kutilganidek ishlamasa yoki juda ko'p ishlov beruvchilar ishga tushsa, hodisa tinglovchilarini tekshirish uchun brauzer ishlab chiquvchi vositalaridan foydalaning. React komponentingizning ishlov beruvchilariga (ayniqsa `onClickCapture` va `onClick`) strategik tarzda joylashtirilgan `console.log` bayonotlari hodisaning ham ushlab olish, ham ko'tarilish fazalari bo'ylab yo'lini kuzatish uchun bebaho bo'lishi mumkin, bu sizga hodisaning qayerda to'xtatilganini yoki to'xtatilganini aniqlashga yordam beradi.
- Bir Nechta Portallar bilan Z-Index Urushlari: Portallar ota-ona elementlarining z-index muammolaridan qochishga yordam bersa-da, agar hujjat ildizida bir nechta yuqori z-indexli elementlar mavjud bo'lsa (masalan, turli komponentlar/kutubxonalardan bir nechta modallar), ular global z-index ziddiyatlarini hal qilmaydi. To'g'ri joylashuv tartibini ta'minlash va ilovangiz bo'ylab izchil vizual ierarxiyani saqlash uchun Portal konteynerlaringiz uchun z-index strategiyangizni diqqat bilan rejalashtiring.
Xulosa: React Portallari bilan Chuqur Hodisalarni Tarqatishni O'zlashtirish
React Portallari dasturchilarga qat'iy DOM ierarxiyalaridan kelib chiqadigan jiddiy uslub va joylashuv muammolarini yengish imkonini beruvchi nihoyatda kuchli vositadir. Biroq, ularning to'liq salohiyatini ochishning kaliti React'ning sintetik hodisalar tizimi ushbu ajratilgan DOM tuzilmalari bo'ylab hodisalar tarqalishini qanday boshqarishini chuqur tushunishda yotadi.
"React Portal hodisa tunneli" tushunchasi React'ning hodisalar oqimi uchun mantiqiy komponent daraxtiga qanday ustunlik berishini nafis tarzda tasvirlaydi. Bu Portalda render qilingan elementlardan keladigan hodisalar, ularning jismoniy DOM joylashuvidan qat'i nazar, o'zlarining konseptual ota-onalari orqali to'g'ri tarqalishini ta'minlaydi. React daraxti orqali ushlab olish fazasi (pastga tunnel) va ko'tarilish fazasidan (yuqoriga ko'tarilish) foydalanib, dasturchilar global tashqarida bosish ishlov beruvchilari kabi barqaror xususiyatlarni amalga oshirishi, kontekstni saqlab qolishi va murakkab o'zaro ta'sirlarni samarali boshqarishi mumkin, bu esa har qanday mintaqadagi turli foydalanuvchilar uchun oldindan aytib bo'ladigan va yuqori sifatli foydalanuvchi tajribasini ta'minlaydi.
Ushbu tushunchani qabul qiling va siz Portallarning hodisalar bilan bog'liq murakkabliklar manbai bo'lishdan yiroq, balki React asboblar to'plamingizning tabiiy va intuitiv qismiga aylanganini ko'rasiz. Bu mahorat sizga murakkab UI talablari va global foydalanuvchi kutishlariga bardosh beradigan murakkab, qulay va samarali foydalanuvchi tajribalarini yaratish imkonini beradi.