Правила хуков и useRef
Запоминаем два железных правила хуков и знакомимся с useRef — «карманом» для значений вне состояния.
Правила хуков — два ограничения, гарантирующие, что React правильно связывает вызовы хуков с компонентом между перерисовками.
Правило 1: только на верхнем уровне
Хуки нельзя вызывать внутри условий, циклов и вложенных функций — только на верхнем уровне компонента. React опирается на порядок вызовов хуков, и этот порядок должен быть одинаковым при каждой отрисовке.
// ❌ Хук внутри условия — порядок «плавает»
function Bad({ show }) {
if (show) {
const [x, setX] = useState(0); // нельзя!
}
}
// ✅ Хук на верхнем уровне, условие — внутри
function Good({ show }) {
const [x, setX] = useState(0);
if (show) { /* используем x */ }
}
Почему так
React не знает имён ваших переменных. Он сопоставляет состояния по порядку: «первый useState, второй useState...». Если на одной отрисовке хук вызван, а на другой — пропущен (из-за условия), порядок собьётся, и React перепутает состояния. Отсюда и правило.
Правило 2: только из React-функций
Хуки вызывают только из функциональных компонентов и из других хуков (кастомных). Из обычных функций — нельзя.
function helper() {
const [x] = useState(0); // ❌ это обычная функция, не компонент
}
useRef: значение без перерисовки
Иногда нужно сохранить значение между перерисовками, но без запуска перерисовки при его изменении. Для этого есть useRef — он создаёт объект с полем .current:
function StopWatch() {
const intervalRef = useRef(null); // переживёт перерисовки
function start() {
intervalRef.current = setInterval(() => {
console.log("тик");
}, 1000);
}
function stop() {
clearInterval(intervalRef.current);
}
// ...
}
Изменение intervalRef.current не вызывает перерисовку — поэтому ref удобен для таймеров, предыдущих значений, флагов.
useRef для доступа к DOM
Главное применение — прямая ссылка на DOM-элемент, например чтобы поставить фокус:
function SearchField() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus(); // фокус при появлении
}, []);
return <input ref={inputRef} />;
}
Проп ref={inputRef} связывает inputRef.current с реальным DOM-узлом после отрисовки.
state vs ref
| useState | useRef | |
| Перерисовка при изменении | да | нет |
| Для чего | данные, влияющие на UI | DOM-узлы, таймеры, «закулисные» значения |
Итог
- Хуки вызывают только на верхнем уровне (не в условиях/циклах) и только из компонентов или кастомных хуков.
- React сопоставляет хуки по порядку вызова — потому порядок должен быть стабилен.
useRefхранит значение между рендерами без перерисовки и даёт ссылку на DOM через пропref.