Порталы React — это мощный инструмент, который позволяет отображать компонент React вне его родительского компонента. Это может быть полезно в различных ситуациях, таких как:
- Создание модальных окон или диалогов
- Добавление уведомлений или предупреждений
- Отображение элементов в другом контексте DOM, например, в
<head>
документа
Чтобы использовать порталы, вам необходимо установить пакет react-dom
:
npm install react-dom
Затем вы можете использовать метод ReactDOM.createPortal()
для создания портала. Этот метод принимает два аргумента: элемент, который нужно отобразить, и элемент DOM, в котором этот элемент должен быть отрисован.
Вот пример простого портала, отображающего модальное окно:
import React from 'react'; import ReactDOM from 'react-dom'; function Modal() { return ReactDOM.createPortal( <div className="modal"> <p>This is a modal</p> </div>, document.body ); }
В этом примере компонент Modal
визуализируется в <body>
документа, за пределами его родительского компонента.
Использование порталов с состоянием
Чтобы использовать порталы с состоянием, вам необходимо создать компонент-контейнер, который управляет состоянием и отображает портал. Вот пример компонента ModalContainer
, который управляет видимостью модального окна:
import React from 'react'; import ReactDOM from 'react-dom'; function Modal({ isOpen }) { return ReactDOM.createPortal( <div className={`modal ${isOpen ? 'is-open' : ''}`}> <p>This is a modal</p> </div>, document.body ); } import React, { useState } from 'react'; import Modal from './Modal'; function ModalContainer() { const [isOpen, setIsOpen] = useState(false); return ( <> <button onClick={() => setIsOpen(true)}>Open Modal</button> <Modal isOpen={isOpen} /> </> ); }
В этом примере компонент ModalContainer
управляет состоянием isOpen
, которое определяет, является ли модальное окно видимым или нет. Когда пользователь нажимает кнопку «Открыть модальное окно», состояние isOpen
устанавливается на true
, что приводит к отображению модального окна.
Закрытие модального окна
Чтобы закрыть модальное окно, вам нужно добавить способ его закрытия пользователем. Вот пример компонента Modal
с кнопкой закрытия:
import React, { useState } from 'react'; import ReactDOM from 'react-dom'; function Modal({ isOpen, onClose }) { return ReactDOM.createPortal( <div className={`modal ${isOpen ? 'is-open' : ''}`}> <button onClick={onClose}>Close</button> <p>This is a modal</p> </div>, document.body ); } function ModalContainer() { const [isOpen, setIsOpen] = useState(false); function handleClose() { setIsOpen(false); } return ( <> <button onClick={() => setIsOpen(true)}>Open Modal</button> <Modal isOpen={isOpen} onClose={handleClose} /> </> ); }
В этом примере компонент Modal
включает кнопку закрытия, которая при нажатии вызывает свойство onClose
. Компонент ModalContainer
передает функцию handleClose
компоненту Modal
в качестве реквизита onClose
, который устанавливает состояние isOpen
в false
и скрывает модальное окно.
Передовые методы
Вот несколько продвинутых методов использования порталов:
Доступ к узлу портала
Если вам нужно получить доступ к узлу DOM портала, вы можете использовать реквизит ref
:
import React, { useRef } from 'react'; import ReactDOM from 'react-dom'; function Modal({ isOpen }) { const modalRef = useRef(); return ReactDOM.createPortal( <div className={`modal ${isOpen ? 'is-open' : ''}`} ref={modalRef}> <p>This is a modal</p> </div>, document.body ); }
В этом примере ссылка modalRef
прикреплена к элементу <div>
, который визуализируется как портал. Вы можете получить доступ к узлу DOM портала, используя свойство current
ссылки:
console.log(modalRef.current); // The DOM node of the portal
Обновление элемента портала
Если вам нужно обновить отображаемый на портале элемент, вы можете использовать метод ReactDOM.unstable_renderSubtreeIntoContainer()
. Этот метод позволяет обновить отображаемый на портале элемент без размонтирования и повторного монтирования компонента портала.
Вот пример компонента Modal
, который использует метод unstable_renderSubtreeIntoContainer()
для обновления элемента, отображаемого на портале:
import React, { useEffect, useRef } from 'react'; import ReactDOM from 'react-dom'; function Modal({ isOpen, children }) { const modalRef = useRef(); const portalElement = useRef(); useEffect(() => { portalElement.current = document.createElement('div'); document.body.appendChild(portalElement.current); return () => { document.body.removeChild(portalElement.current); }; }, []); useEffect(() => { ReactDOM.unstable_renderSubtreeIntoContainer( modalRef.current, <div className={`modal ${isOpen ? 'is-open' : ''}`}> <button onClick={onClose}>Close</button> {children} </div>, portalElement.current ); }, [isOpen, children]); return ReactDOM.createPortal( <div ref={modalRef} />, portalElement.current ); }
В этом примере компонент Modal
использует хук useEffect()
для создания нового элемента DOM, когда компонент монтируется, и для удаления элемента, когда компонент размонтирован. Метод unstable_renderSubtreeIntoContainer()
используется для обновления элемента, отображаемого на портале, при изменении реквизита isOpen
или children
.
Обработка событий клавиатуры
Если вы используете порталы для создания модальных окон или диалогов, вы, вероятно, захотите обрабатывать события клавиатуры, чтобы пользователь мог закрыть модальное окно с помощью клавиши Escape
. Вот пример компонента Modal
, который обрабатывает события клавиатуры:
import React, { useEffect, useRef } from 'react'; import ReactDOM from 'react-dom'; function Modal({ isOpen, onClose }) { const modalRef = useRef(); const portalElement = useRef(); useEffect(() => { portalElement.current = document.createElement('div'); document.body.appendChild(portalElement.current); return () => { document.body.removeChild(portalElement.current); }; }, []); useEffect(() => { const handleKeyDown = (event) => { if (event.key === 'Escape') { onClose(); } }; document.addEventListener('keydown', handleKeyDown); return () => { document.removeEventListener('keydown', handleKeyDown); }; }, [onClose]); useEffect(() => { ReactDOM.unstable_renderSubtreeIntoContainer( import React, { useEffect, useRef } from 'react'; import ReactDOM from 'react-dom'; function Modal({ isOpen, onClose }) { const modalRef = useRef(); const portalElement = useRef(); useEffect(() => { portalElement.current = document.createElement('div'); document.body.appendChild(portalElement.current); return () => { document.body.removeChild(portalElement.current); }; }, []); useEffect(() => { const handleKeyDown = (event) => { if (event.key === 'Escape') { onClose(); } }; document.addEventListener('keydown', handleKeyDown); return () => { document.removeEventListener('keydown', handleKeyDown); }; }, [onClose]); useEffect(() => { ReactDOM.unstable_renderSubtreeIntoContainer( modalRef.current, <div className={`modal ${isOpen ? 'is-open' : ''}`}> <button onClick={onClose}>Close</button> <p>This is a modal</p> </div>, portalElement.current ); }, [isOpen]); return ReactDOM.createPortal( <div ref={modalRef} />, portalElement.current ); } function ModalContainer() { const [isOpen, setIsOpen] = useState(false); function handleOpen() { setIsOpen(true); } function handleClose() { setIsOpen(false); } return ( <> <button onClick={handleOpen}>Open Modal</button> <Modal isOpen={isOpen} onClose={handleClose}
Подведение итогов
Порталы React — это мощный инструмент, который можно использовать в различных ситуациях для рендеринга элементов за пределами их родительского компонента. Они особенно полезны для создания модальных окон, диалогов и уведомлений и могут быть легко интегрированы в ваше приложение React всего несколькими строками кода.
Я надеюсь, что эта статья помогла вам понять, как работают порталы и как эффективно использовать их в ваших собственных проектах. Удачного кодирования!