Children и слоты

В Preact children представляют собой способ передачи вложенного содержимого компонентам. Это мощный инструмент, позволяющий строить компоненты высокой переиспользуемости и создавать гибкие пользовательские интерфейсы.

Основы работы с children

children — это специальное свойство (props.children), которое автоматически содержит все элементы, размещённые внутри тега компонента. Рассмотрим пример:

import { h } from 'preact';

function Card(props) {
    return (
        <div className="card">
            {props.children}
        </div>
    );
}

// Использование
<Card>
    <h2>Заголовок карточки</h2>
    <p>Описание карточки</p>
</Card>

В этом примере компонент Card не знает заранее, какой контент ему будет передан. Он просто выводит всё, что находится между <Card> и </Card>, через props.children.

Особенности children в Preact:

  • children могут быть любыми: элементами Preact, строками, числами или массивами элементов.
  • Даже если внутри компонента ничего нет, props.children существует и равен undefined.

Деструктуризация и множественные children

Для удобства часто используют деструктуризацию:

function Card({ children }) {
    return <div className="card">{children}</div>;
}

Если компонент получает несколько дочерних элементов, children обычно представляет собой массив:

<Card>
    <p>Первый абзац</p>
    <p>Второй абзац</p>
</Card>

В props.children будет массив из двух элементов <p>.

Условная отрисовка и фильтрация children

children можно фильтровать и модифицировать перед рендерингом:

function Card({ children }) {
    const filteredChildren = Array.isArray(children)
        ? children.filter(child => child.type !== 'p')
        : children;
    return <div className="card">{filteredChildren}</div>;
}

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

Слоты (аналог в Preact)

В Preact нет встроенной концепции слотов, как в Vue или Web Components, но можно реализовать похожий паттерн через именованные пропсы.

Пример реализации «слотов»:

function Modal({ header, body, footer }) {
    return (
        <div className="modal">
            <div className="modal-header">{header}</div>
            <div className="modal-body">{body}</div>
            <div className="modal-footer">{footer}</div>
        </div>
    );
}

// Использование
<Modal
    header={<h2>Заголовок модального окна</h2>}
    body={<p>Основное содержимое</p>}
    footer={<button>Закрыть</button>}
/>

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

Динамическое распределение children

Можно комбинировать children и именованные «слоты» для более сложных сценариев:

function Tabs({ children }) {
    return (
        <div className="tabs">
            {Array.isArray(children) ? children.map((tab, index) => (
                <div key={index} className="tab">{tab}</div>
            )) : <div className="tab">{children}</div>}
        </div>
    );
}

// Использование
<Tabs>
    <div>Вкладка 1</div>
    <div>Вкладка 2</div>
</Tabs>

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

Преимущества использования children и слотов

  • Повышение переиспользуемости компонентов.
  • Разделение структуры и содержания.
  • Возможность создавать гибкие, настраиваемые интерфейсы.
  • Сочетаемость с функциональными и классовыми компонентами.

Практические рекомендации

  • Для обработки нескольких children использовать Array.isArray или Children.toArray (из Preact/compat при необходимости).
  • Именованные «слоты» удобно применять для сложных компонентов с фиксированными зонами контента.
  • Для вложенных компонентов стоит придерживаться однородного подхода: либо использовать children для всех вложенных элементов, либо именованные пропсы для ключевых зон.

Использование children и паттерна слотов обеспечивает гибкость архитектуры Preact-приложений и позволяет строить масштабируемые и легко поддерживаемые интерфейсы.