Компоненты высшего порядка (HOC)

Компоненты высшего порядка (Higher-Order Components, HOC) представляют собой функцию, которая принимает компонент и возвращает новый компонент с расширенной функциональностью. Этот подход позволяет повторно использовать логику между компонентами без дублирования кода, сохраняя при этом чистоту и декларативность.


Основная концепция HOC

HOC можно рассматривать как паттерн композиции. Он не изменяет исходный компонент напрямую, а создает обертку, которая добавляет новые свойства, состояние или обработку событий. Простейшая форма HOC выглядит так:

import { h } from 'preact';

function withExtraProps(WrappedComponent) {
  return function EnhancedComponent(props) {
    const newProps = { ...props, extra: 'value' };
    return <WrappedComponent {...newProps} />;
  };
}

В этом примере withExtraProps принимает компонент WrappedComponent и возвращает новый компонент EnhancedComponent, который добавляет дополнительное свойство extra.

Ключевые моменты:

  • HOC — это чистая функция: она не должна изменять исходный компонент.
  • Возвращаемый компонент может быть функциональным или классовым.
  • Основная цель — повторное использование логики, а не модификация JSX.

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

  1. Добавление состояния или обработки событий
import { h } from 'preact';
import { useState } from 'preact/hooks';

function withCounter(WrappedComponent) {
  return function CounterComponent(props) {
    const [count, setCount] = useState(0);

    const increment = () => setCount(count + 1);

    return <WrappedComponent {...props} count={count} increment={increment} />;
  };
}

Такой HOC позволяет любому компоненту получать счетчик и функцию для его увеличения без необходимости самостоятельно реализовывать состояние.

  1. Логирование действий
function withLogging(WrappedComponent) {
  return function LoggingComponent(props) {
    console.log('Rendering component with props:', props);
    return <WrappedComponent {...props} />;
  };
}

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


Передача и объединение пропсов

При использовании HOC важно корректно передавать пропсы: исходные свойства компонента должны сохраняться. Паттерн ...props обеспечивает прозрачную передачу:

function withTheme(WrappedComponent) {
  return function ThemedComponent(props) {
    const theme = { color: 'blue', fontSize: 16 };
    return <WrappedComponent {...props} theme={theme} />;
  };
}

Если HOC должен модифицировать часть пропсов, рекомендуется делать это явно, чтобы избежать неожиданных конфликтов.


Композиция нескольких HOC

Несколько HOC можно комбинировать через цепочку вызовов:

const EnhancedComponent = withLogging(withCounter(MyComponent));

В этом случае сначала применяется withCounter, а затем withLogging. Порядок важен, так как HOC получают пропсы от предыдущего уровня.

Для упрощения композиции часто используют функцию compose:

const compose = (...hocs) => Component =>
  hocs.reduceRight((acc, hoc) => hoc(acc), Component);

const EnhancedComponent = compose(withLogging, withCounter)(MyComponent);

Ограничения и особенности в Preact

  • HOC в Preact работают аналогично React, однако Preact меньше по размеру, поэтому следует избегать чрезмерного создания оберток, чтобы не увеличивать глубину виртуального DOM.
  • Не рекомендуется использовать HOC для изменения рефов (ref), так как обертка скрывает внутренний компонент. Для работы с рефами применяют forwardRef.
  • HOC лучше использовать для чистых логических расширений, а не для сложной структуры JSX или визуальных изменений.

Отличие HOC от хуков

  • HOC создают новые компоненты, добавляя функциональность через композицию.
  • Хуки добавляют состояние и побочные эффекты внутри компонента без создания новых оберток.

HOC часто полезны, когда требуется многоразовое обогащение логики сразу для нескольких компонентов, тогда как хуки более подходят для локального состояния и функциональных компонентов.


Советы по использованию

  1. Держать HOC максимально простыми: один HOC — одна задача.
  2. Всегда передавать исходные пропсы через ...props.
  3. Избегать глубоких цепочек HOC без необходимости, чтобы не усложнять отладку.
  4. Четко документировать, какие пропсы добавляет каждый HOC, чтобы не возникало конфликтов.

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