Фрагменты и порталы

Фрагменты

Фрагменты позволяют группировать несколько элементов без добавления лишних узлов в DOM. В Preact это особенно важно, поскольку каждый лишний узел увеличивает нагрузку на виртуальный DOM и усложняет структуру компонента.

Использование фрагментов реализуется с помощью компонента Fragment:

import { h, Fragment } from 'preact';

function List() {
  return (
    <Fragment>
      <li>Элемент 1</li>
      <li>Элемент 2</li>
      <li>Элемент 3</li>
    </Fragment>
  );
}

Ключевые особенности:

  • Не добавляет дополнительного DOM-узла.
  • Позволяет возвращать несколько элементов из компонента.
  • Может использоваться с ключами для списков, аналогично массивам:
function ItemList({ items }) {
  return (
    <Fragment>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </Fragment>
  );
}

Применение фрагментов с JSX

Фрагменты также поддерживаются в короткой записи с пустыми тегами <>...</>:

function Example() {
  return (
    <>
      <h1>Заголовок</h1>
      <p>Описание</p>
    </>
  );
}

Такой синтаксис делает код компактным, особенно при возвращении нескольких элементов без необходимости импортировать Fragment.

Порталы

Порталы позволяют рендерить элементы вне обычной иерархии DOM-компонента, что особенно важно для модальных окон, всплывающих подсказок и других UI-элементов, которые должны существовать на верхнем уровне документа.

В Preact для создания портала используется функция createPortal из пакета preact/compat:

import { h } from 'preact';
import { createPortal } from 'preact/compat';

function Modal({ children }) {
  const modalRoot = document.getElementById('modal-root');
  return createPortal(
    <div className="modal">
      {children}
    </div>,
    modalRoot
  );
}

Особенности порталов:

  • Элемент рендерится в другом месте DOM, но остаётся частью иерархии компонентов Preact.
  • Сохраняются свойства и контекст родительского компонента.
  • Позволяет управлять стилями и событиями независимо от родительской разметки.

Примеры практического применения

Модальное окно с порталами и фрагментами:

function App() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <button onCl ick={() => setIsOpen(true)}>Открыть модал</button>
      {isOpen && (
        <Modal>
          <Fragment>
            <h2>Заголовок модала</h2>
            <p>Содержимое модала</p>
            <button onCl ick={() => setIsOpen(false)}>Закрыть</button>
          </Fragment>
        </Modal>
      )}
    </>
  );
}

В данном примере:

  • Фрагмент позволяет группировать заголовок, описание и кнопку без лишнего div.
  • Портал рендерит модальное окно в отдельном DOM-узле, что упрощает стилизацию и управление z-index.

Взаимодействие с контекстом

Порталы сохраняют доступ к контексту родительских компонентов:

import { createContext } from 'preact';
const ThemeContext = createContext('light');

function ThemedModal() {
  const theme = useContext(ThemeContext);
  return createPortal(
    <div className={`modal ${theme}`}>Контент с темой {theme}</div>,
    document.getElementById('modal-root')
  );
}

Таким образом, можно использовать порталы без потери контекста, что обеспечивает гибкость при создании сложных интерфейсов.

Рекомендации по использованию

  • Фрагменты применяются для группировки элементов без добавления лишних DOM-узлов, что повышает производительность и читаемость кода.
  • Порталы используются для UI-компонентов, которые должны быть визуально независимы от структуры родителя.
  • Для списков с динамическими элементами рекомендуется всегда указывать key даже внутри фрагментов.

Фрагменты и порталы в Preact позволяют строить чистую и оптимизированную архитектуру компонентов, сохраняя при этом гибкость в управлении DOM и контекстом.