Что такое HOC

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


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

HOC используется для повторного использования логики между компонентами. Вместо дублирования кода в нескольких местах можно создать обёртку, которая управляет общей функциональностью, и применять её к любому компоненту.

Схема работы HOC:

const EnhancedComponent = higherOrderComponent(WrappedComponent);
  • WrappedComponent — исходный компонент, который требует дополнительной логики.
  • higherOrderComponent — функция, которая добавляет функциональность.
  • EnhancedComponent — новый компонент с расширенными возможностями.

Пример базового HOC

Предположим, есть компонент, который выводит текст:

const SimpleComponent = ({ text }) => <div>{text}</div>;

Можно создать HOC, который будет добавлять логирование при рендере:

function withLogging(WrappedComponent) {
  return function(props) {
    console.log('Рендер компонента с пропсами:', props);
    return <WrappedComponent {...props} />;
  };
}

const EnhancedComponent = withLogging(SimpleComponent);

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


HOC и props

HOC обычно работает с пропсами исходного компонента. Для корректной передачи данных важно использовать spread-оператор:

return <WrappedComponent {...props} />;

Это гарантирует, что все пропсы будут переданы обёрнутому компоненту без потерь.


Основные применения HOC

  1. Повторное использование логики Например, авторизация, логирование, обработка ошибок.

  2. Управление состоянием HOC может добавлять состояние или методы для управления внутренними данными компонента.

  3. Обработка данных Например, выборка данных с сервера или фильтрация массива перед передачей компоненту.

  4. Композиция функций Несколько HOC можно комбинировать, создавая цепочку обёрток:

const ComposedComponent = withLogging(withAuth(SimpleComponent));

Принципы работы с HOC

  • Не изменять оригинальный компонент. HOC должен быть чистой функцией, возвращающей новый компонент.
  • Передавать все пропсы дальше. Это обеспечивает гибкость и предотвращает ошибки.
  • Сохранять displayName. Для удобства отладки рекомендуется задавать имя нового компонента:
function withLogging(WrappedComponent) {
  function Enhanced(props) {
    console.log('Рендер:', props);
    return <WrappedComponent {...props} />;
  }

  Enhanced.displayName = `WithLogging(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;

  return Enhanced;
}
  • Не использовать HOC внутри render. Создание HOC в render приведёт к постоянной пересоздаче компонента и потере состояния.

HOC и тестирование с Enzyme

Enzyme позволяет тестировать как исходные компоненты, так и обёрнутые HOC. Важные моменты:

  1. Shallow render Используется для тестирования только компонента верхнего уровня, не включая его детей:
import { shallow } from 'enzyme';

const wrapper = shallow(<EnhancedComponent text="Привет" />);
expect(wrapper.text()).toContain('Привет');
  1. Доступ к WrappedComponent Для проверки внутреннего компонента можно использовать статическое свойство:
const InnerComponent = EnhancedComponent.WrappedComponent || SimpleComponent;
const wrapper = shallow(<InnerComponent text="Привет" />);
  1. Проверка пропсов, переданных HOC Enzyme позволяет убедиться, что HOC корректно прокидывает все данные:
expect(wrapper.props().text).toBe('Привет');

Отличие HOC от Render Props и Hooks

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

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


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

  • Использовать HOC для кросс-компонентной логики, не зависящей от конкретного UI.
  • Держать HOC чистыми и небольшими, добавляя одну ответственность за раз.
  • Комбинировать HOC с композиционным подходом для максимальной гибкости.
  • Для классовых компонентов HOC остаётся предпочтительным инструментом, тогда как для функциональных компонентов чаще применяют Hooks.

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