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

Когда нужен кастомный hook?

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

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

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

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

Когда нужен кастомный хук?

Кастомный хук (custom hook) в React — это механизм, позволяющий извлекать и повторно использовать логику состояния и побочные эффекты из компонентов, сохраняя при этом принцип чистых функций и декларативного описания UI. Его создание оправдано в нескольких ключевых сценариях, которые я, как опытный разработчик, разделю на категории.

Основные критерии для создания кастомного хука

1. Повторное использование логики в разных компонентах

Это основная причина. Если вы обнаруживаете, что один и тот же код, использующий useState, useEffect, useContext или другие хуки, повторяется в нескольких компонентах — это прямой сигнал для создания кастомного хука.

// До: дублирование в компонентах UserList и Profile
function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(false);
  
  useEffect(() => {
    setLoading(true);
    fetch('/api/users')
      .then(res => res.json())
      .then(data => setUsers(data))
      .finally(() => setLoading(false));
  }, []);
  
  return /* ... */;
}

// После: хук useFetch
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    setLoading(true);
    fetch(url)
      .then(res => res.json())
      .then(setData)
      .finally(() => setLoading(false));
  }, [url]);
  
  return { data, loading };
}

// Использование в любом компоненте
function UserList() {
  const { data: users, loading } = useFetch('/api/users');
  return /* ... */;
}

2. Изоляция сложной логики для читаемости

Когда компонент становится слишком большим и сложным из-за множества состояний и эффектов, кастомный хук помогает разделить ответственность. Компонент остается чистым описанием UI, а вся бизнес-логика уходит в хуки.

// Сложный компонент с обработкой формы, валидацией и отправкой
function RegistrationForm() {
  // 5-7 состояний, 3-4 эффекта, несколько обработчиков
  // Трудно читать и поддерживать
}

// Решение: выделить логику в хук useForm
function useForm(initialValues, validationRules) {
  const [values, setValues] = useState(initialValues);
  const [errors, setErrors] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);
  
  const handleChange = (e) => {
    setValues({ ...values, [e.target.name]: e.target.value });
    // Валидация по правилам validationRules
  };
  
  const handleSubmit = async (callback) => {
    setIsSubmitting(true);
    // Асинхронная обработка и валидация
    await callback(values);
    setIsSubmitting(false);
  };
  
  return { values, errors, isSubmitting, handleChange, handleSubmit };
}

// Теперь компонент чистый
function RegistrationForm() {
  const form = useForm({ email: '', password: '' }, validationRules);
  return (
    <form onSubmit={() => form.handleSubmit(submitApiCall)}>
      {/* Поля связаны с form.values и form.handleChange */}
    </form>
  );
}

3. Абстракция над сторонними библиотеками или API

Если вы работаете с сложными внешними библиотеками (например, картами, графиками, WebSocket), кастомный хук создает удобный интерфейс взаимодействия, скрывая детали реализации.

// Хук для WebSocket соединения
function useWebSocket(url) {
  const [messages, setMessages] = useState([]);
  const [connection, setConnection] = useState(null);
  
  useEffect(() => {
    const ws = new WebSocket(url);
    ws.onmessage = (event) => {
      setMessages(prev => [...prev, JSON.parse(event.data)]);
    };
    setConnection(ws);
    
    return () => ws.close();
  }, [url]);
  
  const sendMessage = (data) => {
    if (connection && connection.readyState === WebSocket.OPEN) {
      connection.send(JSON.stringify(data));
    }
  };
  
  return { messages, sendMessage };
}

4. Создание композиции из существующих хуков

Часто кастомный хук не содержит новой логики, а просто комбинирует стандартные хуки для решения типовой задачи. Например, управление модальными окнами или синхронизация с localStorage.

// Хук для синхронизации состояния с localStorage
function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });
  
  const setValue = (value) => {
    try {
      setStoredValue(value);
      window.localStorage.setItem(key, JSON.stringify(value));
    } catch (error) {
      console.error(error);
    }
  };
  
  return [storedValue, setValue];
}

Практические примеры из реальных проектов

В моей практике кастомные хуки часто создаются для:

  • Управление состоянием форм (валидация, сабмит, сброс) — хук useForm.
  • Запросы к API с обработкой загрузки, ошибок и кэширования — хуки useFetch, useQuery.
  • Интернационализация — хук useTranslation для работы с i18n.
  • Аутентификация и доступ к контексту пользователяuseAuth.
  • Работа с таймерами и дебаунсингомuseDebounce, useInterval.
  • Интерактивность с браузерным APIuseMediaQuery, useGeolocation, useOnlineStatus.

Принципы хорошего кастомного хука

  1. Имя начинается с use — это конвенция React для идентификации хуков.
  2. Один хук — одна ответственность — не создавайте "монстров", объединяющих несвязанные задачи.
  3. Чистые функции — хук должен возвращать стабильный интерфейс (объект, массив, функцию) и не вызывать непредсказуемых побочных эффектов вне useEffect.
  4. Документация через код — четкие параметры и возвращаемые значения.

Заключение

Кастомный хук нужен когда логика выходит за рамки описания UI и требует повторного использования или абстракции. Он превращает React из библиотеки для создания компонентов в мощный инструмент для построения архитектуры приложения, где бизнес-логика четко отделена от представления. Это следующий уровень после освоения базовых хуков — переход от "как сделать" к "как правильно организовать". В современных приложениях с использованием TypeScript кастомные хуки также становятся ключевым местом для описания типов данных и контрактов между частями системы.