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

Можно ли использовать условный рендер перед вызовом хуков?

1.7 Middle🔥 111 комментариев
#JavaScript Core

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

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

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

Можно ли использовать условный рендер перед вызовами хуков?

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

Почему это невозможно: архитектурные причины

React полагается на строгий порядок вызова хуков между рендерами одного компонента. Каждый хук внутри компонента создает ячейку в внутреннем "списке" React. Этот список сохраняется между рендерами и позволяет React:

  • Сопоставлять состояния useState и useReducer с правильными вызовами.
  • Запоминать зависимости и вычисления useEffect, useMemo, useCallback.
  • Связывать рефы из useRef с конкретными экземплярами.

Когда вы помещаете вызов хука внутрь условия (включая условный рендер через &&, тернарный оператор ? : или if перед return), вы ломаете этот порядок.

Пример, который вызовет ошибку

// ❌ НЕПРАВИЛЬНО: Хук вызывается условно
function UserProfile({ isLoggedIn }) {
  // Условный рендер ДО хуков
  if (!isLoggedIn) {
    return <p>Пожалуйста, войдите в систему</p>;
  }

  // Эти хуки могут НЕ вызываться, если isLoggedIn === false
  const [user, setUser] = useState(null);
  const subscription = useSubscription(user.id);

  // ... остальной JSX
}

В этом примере при первом рендере, если isLoggedIn равен false, компонент возвращает null или сообщение, и хуки useState и useSubscription не вызываются. React запоминает, что для этого компонента было вызвано 0 хуков. Затем, если isLoggedIn меняется на true, React войдет в функцию компонента, увидит вызов useState (который он ожидает как "первый хук" в списке) и выбросит знаменитую ошибку:

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

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

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

1. Ранний возврат ПОСЛЕ всех хуков

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

// ✅ ПРАВИЛЬНО: Условный рендер после хуков
function UserProfile({ isLoggedIn }) {
  // Все хуки вызываются БЕЗУСЛОВНО в каждом рендере
  const [user, setUser] = useState(null);
  const auth = useContext(AuthContext);

  // Условный возврат на основе состояния/пропсов ПОСЛЕ хуков
  if (!isLoggedIn || !auth.user) {
    return <p>Пожалуйста, войдите в систему</p>;
  }

  // Дальнейшая логика, использующая хуки
  const subscription = useSubscription(user.id);
  // ... остальной JSX
}

2. Условный рендер внутри JSX

Чаще всего условность следует выражать прямо в возвращаемом JSX.

// ✅ ПРАВИЛЬНО: Условие внутри JSX
function Dashboard({ hasUnread }) {
  // Хуки вызываются безусловно
  const [notifications, setNotifications] = useState([]);
  const unreadCount = useMemo(() => notifications.filter(n => !n.read).length, [notifications]);

  return (
    <div>
      {/* Условный рендер в JSX */}
      {hasUnread && <UnreadBadge count={unreadCount} />}
      <MainContent />
    </div>
  );
}

3. Разделение на компоненты

Для сложных условных сценариев лучше разделить логику на несколько компонентов.

// ✅ ПРАВИЛЬНО: Условность вынесена на уровень компонентов
function App() {
  const { user, isLoading } = useAuth();

  if (isLoading) return <Loader />;
  // Разные компоненты с разными наборами хуков
  return user ? <AuthenticatedApp user={user} /> : <PublicApp />;
}

// Теперь каждый компонент имеет стабильный порядок своих хуков
function AuthenticatedApp({ user }) {
  // Эти хуки вызываются только когда user существует
  const [preferences, setPreferences] = useUserPreferences(user.id);
  const posts = useUserPosts(user.id);
  // ...
}

Исключения и тонкости

  • Пользовательские хуки подчиняются тем же правилам. React не отличает встроенные хуки (useState, useEffect) от ваших собственных.
  • Правило "только на верхнем уровне" означает, что хуки нельзя вызывать внутри циклов, условий или вложенных функций. Они должны быть на верхнем уровне тела функционального компонента или пользовательского хука.
  • Linter для правил хуков (ESLint плагин eslint-plugin-react-hooks) статически анализирует код и предупреждает о таких ошибках на этапе разработки. Никогда не отключайте его для продакшн-кода.

Заключение

Попытка использовать условный рендер перед вызовами хуков нарушает внутренний механизм согласования React и ведет к нестабильности состояния компонента. Правильная философия React Hooks требует: сначала безусловно объявляем все данные и побочные эффекты (хуки), а затем — условно рендерим UI на их основе. Это обеспечивает предсказуемость и сохранность состояния между рендерами. Если ваша логика требует кардинально разных наборов хуков — это явный сигнал к разделению на отдельные компоненты.

Можно ли использовать условный рендер перед вызовом хуков? | PrepBro