← Назад к вопросам

В чем разница между Hook и HOC?

2.0 Middle🔥 211 комментариев
#React#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между React Hooks и Higher-Order Components (HOC)

Это два подхода к переиспользованию логики в React. С появлением Hooks в React 16.8, HOC стал менее популярным, но важно понимать различия и когда использовать каждый.

Что такое HOC (Higher-Order Component)

HOC — это функция, которая принимает компонент и возвращает новый компонент с дополнительной функциональностью:

// HOC
function withTheme(Component) {
  return (props) => {
    const [theme, setTheme] = useState('light');
    
    return (
      <Component {...props} theme={theme} setTheme={setTheme} />
    );
  };
}

// Использование
const Button = ({ theme, setTheme }) => (
  <button style={{ background: theme === 'light' ? '#fff' : '#000' }}>
    Toggle Theme
  </button>
);

const EnhancedButton = withTheme(Button);

// В компоненте
function App() {
  return <EnhancedButton />;
}

Что такое Hook

Hook — это функция, которая позволяет "зацепиться" к функциям React (state, lifecycle):

// Custom Hook
function useTheme() {
  const [theme, setTheme] = useState('light');
  
  return { theme, setTheme };
}

// Использование
const Button = () => {
  const { theme, setTheme } = useTheme();
  
  return (
    <button style={{ background: theme === 'light' ? '#fff' : '#000' }}>
      Toggle Theme
    </button>
  );
};

Сравнение: основные различия

1. Способ применения

HOC — оборачивает компонент (composition):

const EnhancedComponent = withFeature(MyComponent);

Hook — внедряется внутри компонента:

function MyComponent() {
  const feature = useFeature();
  // ...
}

2. Использование имён компонентов

HOC создает новый компонент, что может привести к путанице в React DevTools:

// HOC создаёт компонент WithTheme(Button)
const Button = ({ theme }) => <button>{theme}</button>;
const EnhancedButton = withTheme(Button);
// В React DevTools: WithTheme { Button }

// Нужно явно указать displayName
function withTheme(Component) {
  const Wrapped = (props) => {
    const theme = useTheme();
    return <Component {...props} theme={theme} />;
  };
  Wrapped.displayName = `WithTheme(${Component.displayName || Component.name})`;
  return Wrapped;
}

Hook чище:

function Button() {
  const theme = useTheme(); // Явно видно в коде
  return <button>{theme}</button>;
}
// В React DevTools: Button

3. Вложенность (wrapper hell)

Много HOC создают глубокое дерево компонентов:

// HOC "pyramid of doom"
const EnhancedComponent = withTheme(
  withAuth(
    withRouter(
      withLogger(MyComponent)
    )
  )
);

Hook избегает этой проблемы:

function MyComponent() {
  const theme = useTheme();
  const auth = useAuth();
  const router = useRouter();
  const logger = useLogger();
  // Плоская структура
}

4. Props drilling

HOC может привести к путанице с props:

function withAPI(Component) {
  return (props) => {
    const data = fetchAPI();
    return <Component {...props} data={data} />;
  };
}

// Откуда пришёл props 'data'? Неясно, нужно смотреть выше
const Button = ({ data, onClick, label }) => {
  return <button onClick={onClick}>{label}: {data}</button>;
};

Hook ясен:

function Button({ onClick, label }) {
  const data = useAPI(); // Явно указано внутри
  return <button onClick={onClick}>{label}: {data}</button>;
}

5. Static методы

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

const Button = () => <button>Click</button>;
Button.getTheme = () => 'light'; // Static метод

const Enhanced = withTheme(Button);
console.log(Enhanced.getTheme); // undefined! (потеряли метод)

// Нужно явно копировать
import hoistNonReactStatics from 'hoist-non-react-statics';

function withTheme(Component) {
  const Wrapped = (props) => { /* ... */ };
  hoistNonReactStatics(Wrapped, Component);
  return Wrapped;
}

Hook не имеет этой проблемы:

function Button() {
  const theme = useTheme();
  return <button>Click</button>;
}
Button.getTheme = () => 'light';

// Метод на месте
console.log(Button.getTheme); // Function

Полное сравнение

Особенность        | HOC          | Hook
-------------------------------------------------
Апи                | Функция      | Функция в компоненте
Nesting (pyramid)  | Плохо        | Хорошо
Dev Tools          | Сложнее      | Проще
Props drilling     | Возможно     | Явно видно
Static методы      | Нужно копировать | Нет проблем
Производительность | Нормально    | Слегка лучше
Ошибки типов       | TS сложнее   | TS проще
Новые проекты      | Не рекомендуется | Рекомендуется

Когда использовать HOC

Редко, но есть случаи:

  1. Старые проекты — если уже используются
// Легаси код
const withAuth = (Component) => {
  return (props) => {
    const isAuth = useContext(AuthContext);
    return isAuth ? <Component {...props} /> : <Navigate to="/login" />;
  };
};
  1. Оборачивание класс-компонентов (в старых проектах)
class OldComponent extends React.Component {
  render() {
    return <div>{this.props.data}</div>;
  }
}

const Enhanced = withData(OldComponent);
  1. Сложная логика трансформации props (но и Hook справится)

Когда использовать Hook (всегда)

Новые проекты:

// Custom hook для переиспользуемой логики
function useFormValidation(initialState) {
  const [values, setValues] = useState(initialState);
  const [errors, setErrors] = useState({});
  
  const validate = () => {
    const newErrors = {};
    // Валидация
    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };
  
  return { values, setValues, errors, validate };
}

// Использование в компоненте
function LoginForm() {
  const { values, setValues, errors, validate } = useFormValidation({
    email: '',
    password: ''
  });
  
  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      if (validate()) {
        // Отправить
      }
    }}>
      <input
        value={values.email}
        onChange={(e) => setValues({ ...values, email: e.target.value })}
      />
      {errors.email && <span>{errors.email}</span>}
    </form>
  );
}

Реальный пример: миграция HOC -> Hook

До (HOC):

function withLogger(Component) {
  return (props) => {
    useEffect(() => {
      console.log('Component mounted');
      return () => console.log('Component unmounted');
    }, []);
    
    return <Component {...props} />;
  };
}

const Button = () => <button>Click</button>;
export default withLogger(Button);

После (Hook):

function useLogger() {
  useEffect(() => {
    console.log('Component mounted');
    return () => console.log('Component unmounted');
  }, []);
}

function Button() {
  useLogger();
  return <button>Click</button>;
}

export default Button;

Много чище и понятнее!

Заключение

Используй Hooks:

  • Для новых компонентов
  • Для переиспользуемой логики
  • Это современный стандарт React

HOC может использоваться:

  • В старых проектах
  • Для интеграции с библиотеками (редко)
  • Для очень сложных трансформаций (но Hook часто лучше)

Правило: Hooks > HOC. Если можешь использовать Hook, используй его.