Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Нет, нельзя ставить 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 при рендерах:
- Первый рендер:
isVisible = true. React видит вызовuseStateи выделяет для него "ячейку" №1 в своей внутренней цепочке. - Второй рендер:
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 сможет правильно сопоставлять хуки между рендерами и сохранять их состояние, что является основой стабильной работы функциональных компонентов.