Использование useRef для хранения значений
Хук useRef можно использовать не только для доступа к DOM-элементам, но и для хранения любых изменяемых значений, которые должны сохраняться между рендерами, но не должны вызывать перерисовку компонента.
Как это работает
Значение, хранящееся в ref.current, остаётся неизменным в течение всего жизненного цикла компонента, если вы его явно не измените. При этом:
- Изменение
ref.currentне запускает повторный рендер. - Значение доступно во всех обработчиках, эффектах и даже внутри замыканий без потери актуальности (в отличие от переменных в теле компонента).
Типичные сценарии
Хранение предыдущего значения
Полезно для сравнения текущего и предыдущего состояния или пропса:
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
function Counter({ count }) {
const prevCount = usePrevious(count);
return <p>Сейчас: {count}, было: {prevCount}</p>;
}
Управление таймерами
Чтобы избежать создания новых таймеров при каждом рендере:
function Timer() {
const intervalRef = useRef(null);
useEffect(() => {
intervalRef.current = setInterval(() => {
console.log('Тик');
}, 1000);
return () => clearInterval(intervalRef.current);
}, []);
function stopTimer() {
clearInterval(intervalRef.current);
}
return <button onClick={stopTimer}>Остановить</button>;
}
Кэширование дорогих вычислений
Если вычисление зависит от внешних данных и не должно зависеть от ререндера:
function ExpensiveComponent({ data }) {
const cacheRef = useRef(null);
if (!cacheRef.current || cacheRef.current.input !== data) {
cacheRef.current = {
input: data,
output: heavyComputation(data)
};
}
return <div>{cacheRef.current.output}</div>;
}
Отличие от useState
useState— для данных, влияющих на UI (изменение → рендер).useRef— для данных, нужных «внутри» логики, но не отображаемых напрямую.
Например, счётчик попыток авторизации, который не отображается, но используется в фоне — хороший кандидат для ref.
Важные особенности
- Не мутируйте
ref.currentво время рендера (например, внутри основного тела компонента). Это может привести к непредсказуемому поведению. - Значение в
refне сериализуется и не сохраняется при горячей перезагрузке (HMR), если не предусмотрена дополнительная логика.
Использование useRef для хранения значений — это способ сохранить "память" компонента без лишних ререндеров.