useRef
Иногда нужен прямой доступ к элементу страницы: поставить курсор в поле, прокрутить список, измерить размер. Для этого есть хук useRef. Он создаёт ссылку (ref) — объект с единственным свойством current, которое сохраняется между отрисовками. Изменение current не вызывает перерисовку, в этом главное отличие от состояния.
Ссылка на DOM-элемент
Поставим курсор в поле формы добавления задачи по кнопке. В src/components/AddTodoForm.jsx:
// src/components/AddTodoForm.jsx
import { useRef } from 'react';
export default function AddTodoForm() {
const inputRef = useRef(null);
function handleFocus() {
inputRef.current.focus();
}
return (
<>
<input ref={inputRef} placeholder="Новая задача" />
<button onClick={handleFocus}>Ввести задачу</button>
</>
);
}Атрибут ref говорит React: «положи в inputRef.current этот DOM-элемент». После нажатия на кнопку курсор окажется в поле.
Фокус после отправки
Удобно возвращать курсор в поле после добавления задачи, чтобы сразу вводить следующую:
// src/components/AddTodoForm.jsx
function handleSubmit(event) {
event.preventDefault();
onAdd(text);
setText('');
inputRef.current.focus();
}Добавьте задачу — поле очистится, а курсор останется в нём.
Значение без перерисовки
Ref подходит и для данных, которые не выводятся на страницу, например номера таймера. Секундомер — src/components/Stopwatch.jsx:
// src/components/Stopwatch.jsx
import { useRef, useState } from 'react';
export default function Stopwatch() {
const [seconds, setSeconds] = useState(0);
const timerRef = useRef(null);
function start() {
timerRef.current = setInterval(() => setSeconds((value) => value + 1), 1000);
}
function stop() {
clearInterval(timerRef.current);
}
return (
<>
<p>Прошло: {seconds} с</p>
<button onClick={start}>Старт</button>
<button onClick={stop}>Стоп</button>
</>
);
}Номер таймера нужен только кнопке «Стоп», показывать его не нужно, поэтому он хранится в ref, а не в состоянии.
Ref в своём компоненте
В React 19 ref можно передать своему компоненту как обычный проп и прикрепить к элементу внутри:
// src/components/TextInput.jsx
export default function TextInput({ ref, ...props }) {
return <input ref={ref} className="text-input" {...props} />;
}Теперь <TextInput ref={inputRef} /> работает так же, как input. В старых версиях для этого требовалась функция forwardRef.
useRef или useState
useRef | useState | |
|---|---|---|
| Изменение перерисовывает компонент | Нет | Да |
| Как менять | ref.current = значение | Через сеттер |
| Для чего | DOM-элементы, таймеры, служебные значения | Всё, что видно на экране |
Частая ошибка
Во время отрисовки ref.current ещё пуст: React заполняет его, только когда элемент появится на странице. Поэтому обращаться к нему можно в обработчиках событий и эффектах, но не в теле компонента:
// Неправильно: при первой отрисовке inputRef.current равен null.
const inputRef = useRef(null);
inputRef.current.focus();Чтобы поставить фокус сразу после появления, вызовите focus() внутри useEffect или используйте атрибут autoFocus.