Visaptverošs ceļvedis par React createPortal API: portālu izveide, notikumu apstrāde un uzlaboti lietošanas gadījumi elastīgām un pieejamām saskarnēm.
React createPortal: portālu izveides un notikumu apstrādes meistarība
Mūsdienu tīmekļa izstrādē ar React ir būtiski izveidot lietotāja saskarnes, kas nevainojami integrējas ar pamatā esošo dokumenta struktūru. Lai gan React komponenšu modelis lieliski pārvalda virtuālo DOM, dažreiz mums ir nepieciešams renderēt elementus ārpus parastās komponenšu hierarhijas. Šeit noder createPortal. Šis ceļvedis padziļināti apskata createPortal, aptverot tā mērķi, lietošanu un uzlabotas tehnikas notikumu apstrādei un sarežģītu UI elementu veidošanai. Mēs aplūkosim internacionalizācijas apsvērumus, pieejamības labākās prakses un biežākās kļūdas, no kurām jāizvairās.
Kas ir React createPortal?
createPortal ir React API, kas ļauj renderēt React komponentes bērnus citā DOM koka daļā, ārpus vecākkomponentes hierarhijas. Tas ir īpaši noderīgi, veidojot tādus elementus kā modālie logi, rīka padomi, nolaižamās izvēlnes un pārklājumi, kuriem jābūt novietotiem dokumenta augšējā līmenī vai noteiktā konteinerā, neatkarīgi no tā, kur React komponenšu kokā atrodas komponente, kas tos aktivizē.
Bez createPortal, lai to panāktu, bieži vien ir nepieciešami sarežģīti risinājumi, piemēram, tieša DOM manipulācija vai CSS absolūtā pozicionēšana, kas var radīt problēmas ar stacking contexts, z-index konfliktiem un pieejamību.
Kāpēc izmantot createPortal?
Šeit ir galvenie iemesli, kāpēc createPortal ir vērtīgs rīks jūsu React arsenālā:
- Uzlabota DOM struktūra: Izvairās no komponenšu dziļas ligzdošanas DOM, nodrošinot tīrāku un vieglāk pārvaldāmu struktūru. Tas ir īpaši svarīgi sarežģītām lietojumprogrammām ar daudziem interaktīviem elementiem.
- Vienkāršota stilizācija: Viegli pozicionēt elementus attiecībā pret skata logu vai konkrētiem konteineriem, nepaļaujoties uz sarežģītiem CSS trikiem. Tas vienkāršo stilizāciju un izkārtojumu, īpaši strādājot ar elementiem, kuriem jāpārklāj cits saturs.
- Uzlabota pieejamība: Atvieglo pieejamu UI izveidi, ļaujot pārvaldīt fokusu un tastatūras navigāciju neatkarīgi no komponenšu hierarhijas. Piemēram, nodrošinot, ka fokuss paliek modālā loga iekšienē.
- Labāka notikumu apstrāde: Ļauj notikumiem pareizi izplatīties no portāla satura uz React koku, nodrošinot, ka notikumu klausītāji, kas piesaistīti vecākkomponentēm, joprojām darbojas kā paredzēts.
createPortal pamatlietošana
createPortal API pieņem divus argumentus:
- React mezgls (JSX), kuru vēlaties renderēt.
- DOM elements, kurā vēlaties renderēt mezglu. Šim DOM elementam ideālā gadījumā vajadzētu pastāvēt, pirms tiek montēta komponente, kas izmanto
createPortal.
Šeit ir vienkāršs piemērs:
Piemērs: modālā loga renderēšana
Pieņemsim, ka jums ir modālā loga komponente, kuru vēlaties renderēt body elementa beigās.
import React from 'react';
import ReactDOM from 'react-dom';
function Modal({ children, isOpen, onClose }) {
if (!isOpen) return null;
const modalRoot = document.getElementById('modal-root'); // Assumes you have a <div id="modal-root"></div> in your HTML
if (!modalRoot) {
console.error('Modal root element not found!');
return null;
}
return ReactDOM.createPortal(
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
{children}
</div>
</div>,
modalRoot
);
}
export default Modal;
Paskaidrojums:
- Mēs importējam
ReactDOM, jocreatePortalirReactDOMobjekta metode. - Mēs pieņemam, ka jūsu HTML ir DOM elements ar ID
modal-root. Šeit tiks renderēts modālais logs. Pārliecinieties, ka šis elements pastāv. Izplatīta prakse ir pievienot<div id="modal-root"></div>tieši pirms aizverošā</body>taga jūsuindex.htmlfailā. - Mēs izmantojam
ReactDOM.createPortal, lai renderētu modālā loga JSXmodalRootelementā. - Mēs izmantojam
e.stopPropagation(), lai novērstu, kaonClicknotikums modālā loga saturā aktivizēonCloseapstrādātāju uz pārklājuma. Tas nodrošina, ka klikšķis modālā loga iekšpusē to neaizver.
Lietošana:
import React, { useState } from 'react';
import Modal from './Modal';
function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div>
<button onClick={() => setIsModalOpen(true)}>Open Modal</button>
<Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>
<h2>Modal Content</h2>
<p>This is the content of the modal.</p>
<button onClick={() => setIsModalOpen(false)}>Close</button>
</Modal>
</div>
);
}
export default App;
Šis piemērs demonstrē, kā renderēt modālo logu ārpus parastās komponenšu hierarhijas, ļaujot to absolūti pozicionēt lapā. createPortal izmantošana šādā veidā atrisina bieži sastopamas problēmas ar stacking contexts un ļauj viegli izveidot konsekventu modālo logu stilizāciju visā jūsu lietojumprogrammā.
Notikumu apstrāde ar createPortal
Viena no galvenajām createPortal priekšrocībām ir tā, ka tas saglabā React parasto notikumu burbuļošanas (event bubbling) uzvedību. Tas nozīmē, ka notikumi, kas rodas portāla saturā, joprojām izplatīsies augšup pa React komponenšu koku, ļaujot vecākkomponentēm tos apstrādāt.
Tomēr ir svarīgi saprast, kā notikumi tiek apstrādāti, kad tie šķērso portāla robežu.
Piemērs: Notikumu apstrāde ārpus portāla
import React, { useState, useRef, useEffect } from 'react';
import ReactDOM from 'react-dom';
function OutsideClickExample() {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef(null);
const portalRoot = document.getElementById('portal-root');
useEffect(() => {
function handleClickOutside(event) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsOpen(false);
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [dropdownRef]);
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>Toggle Dropdown</button>
{isOpen && portalRoot && ReactDOM.createPortal(
<div ref={dropdownRef} style={{ position: 'absolute', top: '50px', left: '0', border: '1px solid black', padding: '10px', backgroundColor: 'white' }}>
Dropdown Content
</div>,
portalRoot
)}
</div>
);
}
export default OutsideClickExample;
Paskaidrojums:
- Mēs izmantojam
ref, lai piekļūtu nolaižamās izvēlnes elementam, kas renderēts portāla iekšienē. - Mēs pievienojam
mousedownnotikumu klausītājudocument, lai noteiktu klikšķus ārpus nolaižamās izvēlnes. - Notikumu klausītājā mēs pārbaudām, vai klikšķis notika ārpus nolaižamās izvēlnes, izmantojot
dropdownRef.current.contains(event.target). - Ja klikšķis notika ārpus nolaižamās izvēlnes, mēs to aizveram, iestatot
isOpenuzfalse.
Šis piemērs demonstrē, kā apstrādāt notikumus, kas notiek ārpus portāla satura, ļaujot jums izveidot interaktīvus elementus, kas reaģē uz lietotāja darbībām apkārtējā dokumentā.
Paplašināti lietošanas gadījumi
createPortal neaprobežojas tikai ar vienkāršiem modālajiem logiem un rīka padomiem. To var izmantot dažādos sarežģītākos scenārijos, tostarp:
- Konteksta izvēlnes: Dinamiski renderēt konteksta izvēlnes tuvu peles kursoram, nospiežot labo peles pogu.
- Paziņojumi: Rādīt paziņojumus ekrāna augšdaļā, neatkarīgi no komponenšu hierarhijas.
- Pielāgoti uznirstošie logi (popovers): Izveidot pielāgotas uznirstošo logu komponentes ar uzlabotu pozicionēšanu un stilizāciju.
- Integrācija ar trešo pušu bibliotēkām: Izmantot
createPortal, lai integrētu React komponentes ar trešo pušu bibliotēkām, kurām nepieciešamas noteiktas DOM struktūras.
Piemērs: Konteksta izvēlnes izveide
import React, { useState, useRef, useEffect } from 'react';
import ReactDOM from 'react-dom';
function ContextMenuExample() {
const [contextMenu, setContextMenu] = useState(null);
const menuRef = useRef(null);
useEffect(() => {
function handleClickOutside(event) {
if (menuRef.current && !menuRef.current.contains(event.target)) {
setContextMenu(null);
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [menuRef]);
const handleContextMenu = (event) => {
event.preventDefault();
setContextMenu({
x: event.clientX,
y: event.clientY,
});
};
const portalRoot = document.getElementById('portal-root');
return (
<div onContextMenu={handleContextMenu} style={{ border: '1px solid black', padding: '20px' }}>
Right-click here to open context menu
{contextMenu && portalRoot && ReactDOM.createPortal(
<div
ref={menuRef}
style={{
position: 'absolute',
top: contextMenu.y,
left: contextMenu.x,
border: '1px solid black',
padding: '10px',
backgroundColor: 'white',
}}
>
<ul>
<li>Option 1</li>
<li>Option 2</li>
<li>Option 3</li>
</ul>
</div>,
portalRoot
)}
</div>
);
}
export default ContextMenuExample;
Paskaidrojums:
- Mēs izmantojam
onContextMenunotikumu, lai noteiktu labo klikšķi uz mērķa elementa. - Mēs novēršam noklusējuma konteksta izvēlnes parādīšanos, izmantojot
event.preventDefault(). - Mēs saglabājam peles koordinātas
contextMenustāvokļa mainīgajā. - Mēs renderējam konteksta izvēlni portālā, pozicionējot to pie peles koordinātām.
- Mēs iekļaujam to pašu loģiku klikšķa noteikšanai ārpusē, kā iepriekšējā piemērā, lai aizvērtu konteksta izvēlni, kad lietotājs noklikšķina ārpus tās.
Pieejamības apsvērumi
Lietojot createPortal, ir būtiski ņemt vērā pieejamību, lai nodrošinātu, ka jūsu lietojumprogramma ir lietojama visiem.
Fokusa pārvaldība
Kad atveras portāls (piemēram, modālais logs), jums jānodrošina, ka fokuss tiek automātiski pārvietots uz pirmo interaktīvo elementu portālā. Tas palīdz lietotājiem, kas navigē ar tastatūru vai ekrāna lasītāju, viegli piekļūt portāla saturam.
Kad portāls tiek aizvērts, jums jāatgriež fokuss uz elementu, kas izsauca portāla atvēršanu. Tas uztur konsekventu navigācijas plūsmu.
ARIA atribūti
Izmantojiet ARIA atribūtus, lai sniegtu semantisku informāciju par portāla saturu. Piemēram, izmantojiet aria-modal="true" uz modālā loga elementa, lai norādītu, ka tas ir modālais dialogs. Izmantojiet aria-labelledby, lai saistītu modālo logu ar tā virsrakstu, un aria-describedby, lai saistītu to ar tā aprakstu.
Navigācija ar tastatūru
Nodrošiniet, lai lietotāji varētu navigēt portāla saturā, izmantojot tastatūru. Izmantojiet tabindex atribūtu, lai kontrolētu fokusa secību, un nodrošiniet, ka visi interaktīvie elementi ir sasniedzami ar tastatūru.
Apsveriet fokusa iesprostošanu portālā, lai lietotāji nevarētu nejauši iziet ārpus tā. To var panākt, klausoties Tab taustiņa notikumu un programmatiski pārvietojot fokusu uz pirmo vai pēdējo interaktīvo elementu portālā.
Piemērs: Pieejams modālais logs
import React, { useState, useRef, useEffect } from 'react';
import ReactDOM from 'react-dom';
function AccessibleModal({ children, isOpen, onClose, labelledBy, describedBy }) {
const modalRef = useRef(null);
const firstFocusableElementRef = useRef(null);
const [previouslyFocusedElement, setPreviouslyFocusedElement] = useState(null);
const modalRoot = document.getElementById('modal-root');
useEffect(() => {
if (isOpen) {
// Save the currently focused element before opening the modal.
setPreviouslyFocusedElement(document.activeElement);
// Focus the first focusable element in the modal.
if (firstFocusableElementRef.current) {
firstFocusableElementRef.current.focus();
}
// Trap focus within the modal.
function handleKeyDown(event) {
if (event.key === 'Tab') {
const focusableElements = modalRef.current.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstFocusableElement = focusableElements[0];
const lastFocusableElement = focusableElements[focusableElements.length - 1];
if (event.shiftKey) {
// Shift + Tab
if (document.activeElement === firstFocusableElement) {
lastFocusableElement.focus();
event.preventDefault();
}
} else {
// Tab
if (document.activeElement === lastFocusableElement) {
firstFocusableElement.focus();
event.preventDefault();
}
}
}
}
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('keydown', handleKeyDown);
// Restore focus to the element that had focus before opening the modal.
if(previouslyFocusedElement && previouslyFocusedElement.focus) {
previouslyFocusedElement.focus();
}
};
}
}, [isOpen, previouslyFocusedElement]);
if (!isOpen) return null;
return ReactDOM.createPortal(
<div
className="modal-overlay"
onClick={onClose}
aria-modal="true"
aria-labelledby={labelledBy}
aria-describedby={describedBy}
ref={modalRef}
>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<h2 id={labelledBy}>Modal Title</h2>
<p id={describedBy}>This is the modal content.</p>
<button ref={firstFocusableElementRef} onClick={onClose}>
Close
</button>
{children}
</div>
</div>,
modalRoot
);
}
export default AccessibleModal;
Paskaidrojums:
- Mēs izmantojam ARIA atribūtus, piemēram,
aria-modal,aria-labelledbyunaria-describedby, lai sniegtu semantisku informāciju par modālo logu. - Mēs izmantojam
useEffecthook, lai pārvaldītu fokusu, kad modālais logs tiek atvērts un aizvērts. - Mēs saglabājam pašlaik fokusēto elementu pirms modālā loga atvēršanas un atjaunojam fokusu uz to, kad modālais logs tiek aizvērts.
- Mēs iesprostojam fokusu modālā loga iekšienē, izmantojot
keydownnotikumu klausītāju.
Internacionalizācijas (i18n) apsvērumi
Izstrādājot lietojumprogrammas globālai auditorijai, internacionalizācija (i18n) ir kritisks apsvērums. Lietojot createPortal, ir jāpatur prātā daži punkti:
- Teksta virziens (RTL/LTR): Nodrošiniet, ka jūsu stilizācija atbilst gan no kreisās uz labo (LTR), gan no labās uz kreiso (RTL) puses rakstītām valodām. Tas var ietvert loģisko īpašību izmantošanu CSS (piemēram,
margin-inline-start, nevismargin-left) un atbilstošudiratribūta iestatīšanu HTML elementam. - Satura lokalizācija: Visam tekstam portālā jābūt lokalizētam atbilstoši lietotāja vēlamajai valodai. Izmantojiet i18n bibliotēku (piemēram,
react-intl,i18next), lai pārvaldītu tulkojumus. - Skaitļu un datumu formatēšana: Formatējiet skaitļus un datumus atbilstoši lietotāja lokalizācijai.
IntlAPI nodrošina funkcionalitāti šim nolūkam. - Kultūras konvencijas: Esiet informēti par kultūras konvencijām, kas saistītas ar UI elementiem. Piemēram, pogu izvietojums dažādās kultūrās var atšķirties.
Piemērs: i18n ar react-intl
import React from 'react';
import { FormattedMessage } from 'react-intl';
function MyComponent() {
return (
<div>
<FormattedMessage id="myComponent.greeting" defaultMessage="Hello, world!" />
</div>
);
}
export default MyComponent;
FormattedMessage komponente no react-intl iegūst tulkoto ziņojumu, pamatojoties uz lietotāja lokalizāciju. Konfigurējiet react-intl ar saviem tulkojumiem dažādām valodām.
Biežākās kļūdas un risinājumi
Lai gan createPortal ir spēcīgs rīks, ir svarīgi apzināties dažas bieži sastopamas kļūdas un to, kā no tām izvairīties:
- Trūkst portāla saknes elementa: Pārliecinieties, ka DOM elements, kuru izmantojat kā portāla sakni, pastāv, pirms tiek montēta komponente, kas izmanto
createPortal. Laba prakse ir to ievietot tiešiindex.html. - Z-index konflikti: Esiet uzmanīgi ar z-index vērtībām, pozicionējot elementus ar
createPortal. Izmantojiet CSS, lai pārvaldītu stacking contexts un nodrošinātu, ka jūsu portāla saturs tiek parādīts pareizi. - Notikumu apstrādes problēmas: Izprotiet, kā notikumi izplatās caur portālu, un apstrādājiet tos atbilstoši. Izmantojiet
e.stopPropagation(), lai novērstu notikumu izraisītas neparedzētas darbības. - Atmiņas noplūdes: Pareizi notīriet notikumu klausītājus un atsauces, kad komponente, kas izmanto
createPortal, tiek demontēta, lai izvairītos no atmiņas noplūdēm. IzmantojietuseEffecthook ar tīrīšanas funkciju, lai to panāktu. - Negaidītas ritināšanas problēmas: Portāli dažkārt var traucēt lapas paredzēto ritināšanas uzvedību. Pārliecinieties, ka jūsu stili neaizliedz ritināšanu un ka modālie elementi neizraisa lapas lēcienus vai negaidītu ritināšanas uzvedību, kad tie atveras un aizveras.
Nobeigums
React.createPortal ir vērtīgs rīks, lai izveidotu elastīgas, pieejamas un uzturamas lietotāja saskarnes React vidē. Izprotot tā mērķi, lietošanu un uzlabotās tehnikas notikumu apstrādei un pieejamībai, jūs varat izmantot tā jaudu, lai veidotu sarežģītas un saistošas tīmekļa lietojumprogrammas, kas nodrošina izcilu lietotāja pieredzi globālai auditorijai. Atcerieties ņemt vērā internacionalizācijas un pieejamības labākās prakses, lai nodrošinātu, ka jūsu lietojumprogrammas ir iekļaujošas un lietojamas visiem.
Ievērojot šajā ceļvedī sniegtās vadlīnijas un piemērus, jūs varat droši izmantot createPortal, lai atrisinātu bieži sastopamas UI problēmas un radītu satriecošu tīmekļa pieredzi.