Что такое Higher Order Component (HOC) в React?

MiddleReact · Frontend·Обновлено 1 июля 2026
Коротко
Higher Order Component — это функция, которая принимает компонент и возвращает новый компонент с расширенной функциональностью. HOC — это паттерн для повторного использования логики между компонентами без изменения их исходного кода.

Higher Order Component (HOC)

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

Основная идея

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

// Простой HOC, добавляющий логирование
function withLogging<T extends object>(WrappedComponent: React.ComponentType<T>) {
  return function LoggedComponent(props: T) {
    console.log('Рендер компонента:', WrappedComponent.displayName);
    return <WrappedComponent {...props} />;
  };
}

Типичные сценарии применения

Защита маршрутов (авторизация):

function withAuth<T extends object>(WrappedComponent: React.ComponentType<T>) {
  return function AuthGuard(props: T) {
    const isAuthenticated = useSelector((state) => state.auth.isAuthenticated);

    if (!isAuthenticated) {
      return <Navigate to="/login" replace />;
    }

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

// Использование
const ProtectedDashboard = withAuth(Dashboard);

Добавление данных из внешнего источника:

function withUser<T extends { user?: User }>(WrappedComponent: React.ComponentType<T>) {
  return function WithUserComponent(props: Omit<T, 'user'>) {
    const user = useCurrentUser();
    return <WrappedComponent {...(props as T)} user={user} />;
  };
}

Важные правила при написании HOC

  1. Не мутировать оригинальный компонент — всегда возвращать новый
  2. Пробрасывать все props через {...props}, чтобы не потерять интерфейс компонента
  3. Устанавливать displayName для читаемой отладки в React DevTools
  4. Не использовать HOC внутри render — это приведёт к постоянному пересозданию компонента
function withFeature<T extends object>(WrappedComponent: React.ComponentType<T>) {
  function EnhancedComponent(props: T) {
    return <WrappedComponent {...props} />;
  }

  // Устанавливаем displayName для DevTools
  EnhancedComponent.displayName = `withFeature(${WrappedComponent.displayName || WrappedComponent.name})`;

  return EnhancedComponent;
}

HOC vs Hooks

С появлением хуков в React 16.8 многие задачи, которые раньше решались HOC, теперь решаются с помощью кастомных хуков. Хуки проще в отладке, не создают лишних обёрток в дереве компонентов и не имеют проблем с коллизиями имён props. Однако HOC по-прежнему актуальны для:

  • Компонентов на классах, где хуки недоступны
  • Случаев, когда нужно условно рендерить компонент целиком
  • Интеграции со сторонними библиотеками (например, connect из React-Redux)

Композиция HOC

HOC можно комбинировать, создавая цепочки:

const EnhancedComponent = withAuth(withLogging(withUser(MyComponent)));
// Или через compose из Redux / Ramda
const enhance = compose(withAuth, withLogging, withUser);
const EnhancedComponent = enhance(MyComponent);

Что хочет услышать интервьюер

Чёткое определение HOC как функции, принимающей компонент и возвращающей новый компонент

Понимание цели HOC — переиспользование логики без дублирования кода

Знание правил: не мутировать компонент, пробрасывать все props, устанавливать displayName

Осознание, что HOC — это паттерн, а не API React, и его связь с функциями высшего порядка

Понимание компромисса между HOC и кастомными хуками: когда что предпочтительнее

Пример: HOC для защиты маршрута (авторизация)

import React from 'react';
import { Navigate } from 'react-router-dom';
import { useSelector } from 'react-redux';

function withAuth<T extends object>(WrappedComponent: React.ComponentType<T>) {
  function AuthGuard(props: T) {
    const isAuthenticated = useSelector(
      (state: { auth: { isAuthenticated: boolean } }) => state.auth.isAuthenticated
    );

    if (!isAuthenticated) {
      // Перенаправляем на страницу входа, если пользователь не авторизован
      return <Navigate to="/login" replace />;
    }

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

  // Улучшаем читаемость в React DevTools
  AuthGuard.displayName = `withAuth(${WrappedComponent.displayName || WrappedComponent.name})`;

  return AuthGuard;
}

// Оборачиваем компонент дашборда защитой
const ProtectedDashboard = withAuth(Dashboard);
export default ProtectedDashboard;

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

import { compose } from 'redux';

// Каждый HOC добавляет одну ответственность
const withLogging = <T extends object>(Component: React.ComponentType<T>) => {
  function Logged(props: T) {
    console.log(`[Рендер] ${Component.displayName || Component.name}`);
    return <Component {...props} />;
  }
  Logged.displayName = `withLogging(${Component.displayName || Component.name})`;
  return Logged;
};

const withErrorBoundary = <T extends object>(Component: React.ComponentType<T>) => {
  // ... реализация обработки ошибок
  return Component;
};

// Применяем несколько HOC через compose
const enhance = compose(withAuth, withLogging, withErrorBoundary);
const EnhancedProfile = enhance(ProfilePage);

Типичные ошибки

Путают HOC с обычным компонентом-обёрткой — не понимают, что HOC это функция, возвращающая функцию

Забывают пробрасывать props через {...props}, тем самым ломая интерфейс оборачиваемого компонента

Создают HOC внутри метода render или тела функционального компонента, что вызывает пересоздание на каждый рендер

Не устанавливают displayName, что затрудняет отладку в React DevTools

Не знают современных альтернатив — кастомных хуков — и когда HOC всё ещё предпочтительнее

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.5
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее