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

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

2.3 Middle🔥 262 комментариев
#JavaScript Core

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

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

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

Почему хуки React нельзя вызывать условно

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

Механизм работы хуков "под капотом"

Когда компонент использует хуки, React хранит их состояние в внутреннем связанном списке (linked list) для каждого компонента. Порядок вызовов хуков при первом рендере фиксируется, и при последующих рендерах React ожидает точно такой же порядок. Вот упрощенная иллюстрация:

function MyComponent() {
  const [name, setName] = useState('Анна');   // Хук #1 в списке
  const [age, setAge] = useState(30);         // Хук #2 в списке
  useEffect(() => { /* ... */ });             // Хук #3 в списке
  // ...
}

React запоминает, что первый вызов useState соответствует состоянию name, второй — age, третий (эффект) — определенной логике. На втором и последующих рендерах React проходит по этому списку в том же порядке, чтобы предоставить корректные значения.

Что происходит при условном вызове?

Если мы нарушаем порядок, помещая хук в условие, React "теряет" синхронизацию между вызовами. Рассмотрим проблемный пример:

function ProblematicComponent({ isAdmin }) {
  if (isAdmin) {
    const [adminData, setAdminData] = useState(null); // Условный хук!
  }
  const [userData, setUserData] = useState({});       // Безусловный хук

  // При первом рендере (isAdmin = true):
  // 1. Хук #1: adminData
  // 2. Хук #2: userData

  // При втором рендере (isAdmin = false):
  // 1. Хук #1: Ожидается adminData, но условие пропускается!
  // 2. Хук #2: React попытается дать userData, но это будет несоответствие!
  // -> Нарушение порядка, потенциальные баги и краши.
}

В этом сценарии при изменении isAdmin с true на false React на втором рендере:

  • Ожидает первый элемент в списке — состояние adminData
  • Но фактически первым выполняется useState для userData
  • React выдаст userData как значение для "первого" слота, что приведет к непредсказуемому поведению, смешиванию состояний или ошибкам.

Ошибка: "React Hook "useState" is called conditionally"

React детектирует такие ситуации на этапе разработки с помощью eslint-plugin-react-hooks и выбрасывает известную ошибку:

React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render.

Это защитный механизм, предотвращающий трудноуловимые баги.

Правильные альтернативы

Вместо условного вызова хука нужно использовать условную логику внутри хука или условное отображение компонентов:

1. Условие внутри эффекта или вычисляемого значения:

function CorrectComponent({ isAdmin }) {
  const [userData, setUserData] = useState({});
  const [adminData, setAdminData] = useState(null); // Всегда вызываем!

  useEffect(() => {
    if (isAdmin) {
      // Загружаем данные только для админа
      fetchAdminData().then(setAdminData);
    }
  }, [isAdmin]);
}

2. Условный рендеринг дочерних компонентов:

function ParentComponent({ isAdmin }) {
  return (
    <div>
      {isAdmin && <AdminPanel />} {/* AdminPanel содержит свои хуки */}
      <UserPanel />
    </div>
  );
}

function AdminPanel() {
  const [adminData, setAdminData] = useState(null); // Хук вызывается стабильно
  // ...
}

3. Кастомный хук со встроенной логикой:

function useAdminData(isAdmin) {
  const [adminData, setAdminData] = useState(null);
  
  useEffect(() => {
    if (isAdmin) { /* логика */ }
  }, [isAdmin]);
  
  return adminData;
}

function Component({ isAdmin }) {
  const adminData = useAdminData(isAdmin); // Хук всегда вызывается
  // ...
}

Ключевые выводы

  • Стабильность порядка — фундаментальное требование для корректной работы хуков.
  • Условная логика данных, а не вызова — правильный подход.
  • ESLint-правила — ваш главный союзник в предотвращении таких ошибок.
  • Композиция компонентов часто является самым чистым решением для условной функциональности.

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