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

Можно ли ставить if перед hook?

2.0 Middle🔥 251 комментариев
#React

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

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

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

Нет, нельзя ставить if перед хуком React

Это строгое и фундаментальное правило Hooks в React, сформулированное в документации. Нарушение этого правила приведет к ошибке: React Hook "useState" is called conditionally....

Почему это запрещено: внутренняя механика React Hooks

React полагается на порядок вызова хуков между рендерами для корректного управления их внутренним состоянием. Представьте, что внутри React для каждого компонента существует "цепочка вызовов" (call order), куда последовательно записываются все хуки при рендере.

Пример некорректного кода:

function MyComponent({ isVisible }) {
  if (isVisible) {
    // ❌ НЕПРАВИЛЬНО: хук вызывается условно!
    const [state, setState] = useState(null);
  }
  return <div />;
}

Что происходит внутри React при рендерах:

  1. Первый рендер: isVisible = true. React видит вызов useState и выделяет для него "ячейку" №1 в своей внутренней цепочке.
  2. Второй рендер: isVisible = false. React не видит вызова useState в этой позиции (потому что он внутри if). Он идет по цепочке и пытается считать значение из "ячейки" №1, но ожидает там useState, а находит что-то другое (или ничего). Это ломает внутреннее соответствие и приводит к ошибке или неожиданному поведению (например, один хук получает состояние, предназначенное для другого).

Правильные паттерны для условной логики с хуками

1. Условие ВНУТРИ хука или эффекта

Поместите саму логику условия внутрь хука. Это самый частый случай.

function MyComponent({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // ✅ ПРАВИЛЬНО: Условие внутри useEffect
    if (userId) {
      fetchUser(userId).then(setUser);
    }
  }, [userId]);

  const displayName = user
    ? `${user.firstName} ${user.lastName}` // ✅ Условие внутри рендеринга
    : 'Loading...';

  return <div>{displayName}</div>;
}

2. Условный рендер ДОЧЕРНИХ компонентов с хуками

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

function ParentComponent({ shouldRenderForm }) {
  return (
    <div>
      {shouldRenderForm ? (
        // ✅ ПРАВИЛЬНО: Хук находится внутри дочернего компонента, который рендерится условно.
        <FormWithHooks />
      ) : (
        <p>Форма скрыта</p>
      )}
    </div>
  );
}

// Хуки вызываются гарантированно при каждом рендере FormWithHooks
function FormWithHooks() {
  const [value, setValue] = useState(''); // ✅ Хук всегда вызывается
  return <input value={value} onChange={e => setValue(e.target.value)} />;
}

3. Использование паттерна "ленивая инициализация" (Lazy Initial State)

Для useState можно передать функцию-инициализатор. Это не условный вызов хука, а условное вычисление начального значения.

const [state, setState] = useState(() => {
  // ✅ ПРАВИЛЬНО: Вычисление начального состояния условно, но сам вызов useState — нет.
  const expensiveValue = localStorage.getItem('myKey');
  return expensiveValue ? JSON.parse(expensiveValue) : getDefaultValue();
});

Итог и ключевой принцип

  • Правило: Все хуки (useState, useEffect, useMemo, ваши кастомные хуки) должны вызываться на верхнем уровне функции-компонента (или кастомного хука) при каждом рендере.
  • Нельзя: Помещать их внутрь if, for, while, условных тернарных операторов ? : (кроме как в дочерних компонентах), функций-обработчиков событий или вложенных функций.
  • Можно и нужно: Помещать саму условную логику внутрь тела хука (например, условие в useEffect) или использовать условный рендеринг дочерних компонентов.

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

Можно ли ставить if перед hook? | PrepBro