Работай с DOM и храни значения экземпляра через useRef, не нарушая чистоту рендера.
Открыть этот урок в KodokonuseRef возвращает изменяемую коробку - объект с единственным свойством current - идентичность которой переживает любой рендер. Два принципиальных отличия от состояния: изменение ref.current не вызывает перерисовку, а сама правка видна сразу, без ожидания следующего рендера. Отсюда две семьи применений: ссылка на DOM-узел и хранение значения экземпляра, которое должно пережить рендеры, не управляя отображением.
import { useEffect, useRef } from "react";
function SearchField() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
return <input ref={inputRef} type="search" />;
}Со стороны значений экземпляра в реф попадает всё, что должно сохраняться, не вызывая рендера: идентификатор, который возвращает setInterval, экземпляр внешней библиотеки (карта, редактор, видеоплеер) или значение из предыдущего рендера. Золотое правило остаётся прежним: рендер должен быть чистой функцией от пропсов и состояния. Рефы читают и записывают в эффектах и обработчиках событий, а не во время рендера.
import { useEffect, useRef } from "react";
function usePrevious(value) {
const ref = useRef(undefined);
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}Чтобы отдать DOM-узел наружу из своего компонента, передай реф дальше: начиная с React 19 ref - обычный пропс функциональных компонентов; раньше без forwardRef было не обойтись. И последний рефлекс, который стоит выработать: предпочитай декларативный подход, когда состояния достаточно, чтобы описать результат (CSS-класс, атрибут disabled). Оставь рефы для территории, которую React не покрывает: фокус, выделение текста, измерение элемента, прокрутка, воспроизведение медиа.
import { useRef } from "react";
function TextField({ ref, ...props }) {
return <input ref={ref} {...props} />;
}
function Form() {
const fieldRef = useRef(null);
return (
<>
<TextField ref={fieldRef} />
<button onClick={() => fieldRef.current.focus()}>
Give focus
</button>
</>
);
}