Одной из ключевых концепций современного подхода к разработке пользовательских интерфейсов является выбор между композицией компонентов и использованием наследования. Preact, как облегчённая и высокопроизводительная альтернатива React, полностью поддерживает модель композиции, которая является основным паттерном построения UI.
Наследование предполагает создание иерархий классов, где дочерние компоненты расширяют базовые. На практике это создаёт несколько ограничений:
В Preact компоненты обычно реализуются как функции или классы. Даже классовый подход не рекомендуется использовать для глубокого наследования из-за лёгкости и гибкости функциональных компонентов.
Композиция компонентов заключается в том, что сложные интерфейсы строятся из более простых, без привязки к иерархии классов:
В Preact композиция реализуется через вложенные компоненты, children, props и контексты.
Пример функциональной композиции:
import { h } from 'preact';
function Button({ label, onClick }) {
return <button onCl ick={onClick}>{label}</button>;
}
function Toolbar() {
return (
<div>
<Button label="Сохранить" onCl ick={() => console.log('Сохранено')} />
<Button label="Отмена" onCl ick={() => console.log('Отменено')} />
</div>
);
}
В этом примере Toolbar компонует
несколько кнопок без необходимости наследовать их функциональность.
children для композицииСвойство children позволяет передавать внутрь компонента
любые элементы, обеспечивая динамическую
композицию:
function Card({ children, title }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-content">{children}</div>
</div>
);
}
function App() {
return (
<Card title="Новости">
<p>Сегодня произошло важное событие.</p>
<p>Подробности внутри.</p>
</Card>
);
}
Таким образом, один и тот же компонент Card может быть
переиспользован с любым содержимым, избегая создания наследников для
каждой вариации.
Preact поддерживает контекст (Context API) для
передачи данных через дерево компонентов без необходимости прокидывания
props через все уровни. Это особенно важно для больших
приложений:
import { createContext } from 'preact';
import { useContext } from 'preact/hooks';
const ThemeContext = createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button className={theme}>Кнопка</button>;
}
function App() {
return (
<ThemeContext.Provider value="dark">
<ThemedButton />
</ThemeContext.Provider>
);
}
Контексты позволяют разделять ответственность между компонентами, сохраняя модульность и композиционную структуру.
Higher-Order Components (HOC) и Render Props — это способы композиции, которые заменяют наследование:
prop и использует её для рендеринга.Пример HOC:
function withLogger(Component) {
return function Wrapped(props) {
console.log('Рендер компонента', Component.name);
return <Component {...props} />;
};
}
const LoggedButton = withLogger(Button);
Пример Render Props:
function DataFetcher({ render }) {
const data = ['a', 'b', 'c'];
return render(data);
}
function App() {
return (
<DataFetcher render={(items) => <ul>{items.map(i => <li>{i}</li>)}</ul>} />
);
}
Оба подхода позволяют добавлять поведение без наследования, что делает архитектуру более гибкой.
В Preact предпочтение отдается композиции компонентов перед наследованием. Это снижает связность, повышает повторное использование и упрощает поддержку приложений. Наследование оставлено для редких случаев, где требуется расширение классовой логики, но его использование в UI-компонентах считается устаревшей практикой.
Основная идея — строить интерфейсы как набор независимых
блоков, комбинируя их через props,
children, контексты и функциональные паттерны вроде HOC и
Render Props. Это обеспечивает максимальную гибкость и масштабируемость
приложения.