Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Зачем нужен useRef в React
useRef — это React хук, который позволяет создавать мутируемую ссылку на DOM элемент или значение, которое сохраняется между рендерами компонента. Это один из самых неправильно используемых хуков, но когда его применяют правильно, он очень полезен.
Основные различия useRef от useState
import { useState, useRef } from 'react';
// useState — при изменении триггерит re-render
const [count, setCount] = useState(0);
// Изменение count → re-render → счетчик на экране обновляется
// useRef — НЕ триггерит re-render
const countRef = useRef(0);
// Изменение countRef.current → БЕЗ re-render → значение изменилось, но компонент не перерисовался
Визуально:
setState → Re-render → UI обновляется
↓
updateRef → БЕЗ re-render → только значение в памяти изменилось
1. Прямой доступ к DOM элементам
Это самое частое использование useRef — получить прямой доступ к HTML элементу.
// Фокус на input при загрузке
function SearchInput() {
const inputRef = useRef(null);
useEffect(() => {
// Фокус на input элемент при монтировании
inputRef.current?.focus();
}, []);
return (
<input
ref={inputRef}
type="text"
placeholder="Search..."
/>
);
}
Реальный пример:
// Видеоплеер с кнопками управления
function VideoPlayer() {
const videoRef = useRef(null);
const handlePlay = () => {
videoRef.current?.play();
};
const handlePause = () => {
videoRef.current?.pause();
};
const handleMute = () => {
videoRef.current.muted = !videoRef.current.muted;
};
return (
<div>
<video ref={videoRef} src="movie.mp4" width="400" />
<button onClick={handlePlay}>Play</button>
<button onClick={handlePause}>Pause</button>
<button onClick={handleMute}>Mute</button>
</div>
);
}
2. Хранение значения, которое не должно триггерить re-render
// Запись количества отправленных запросов
// Не нужно обновлять UI при каждом запросе
function DataFetcher() {
const requestCountRef = useRef(0);
const [data, setData] = useState(null);
const fetchData = async () => {
requestCountRef.current++;
console.log(`Request #${requestCountRef.current}`);
const response = await fetch('/api/data');
setData(await response.json());
};
return (
<div>
<button onClick={fetchData}>Fetch Data</button>
<p>Data: {data ? JSON.stringify(data) : 'Loading...'}</p>
{/* requestCountRef.current не выводим на экран, он только для логирования */}
</div>
);
}
Пример: Контроль интервала
function Timer() {
const [seconds, setSeconds] = useState(0);
const intervalRef = useRef(null);
const startTimer = () => {
// Сохраняем ID интервала в ref
// Чтобы потом иметь возможность его остановить
intervalRef.current = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
};
const stopTimer = () => {
// Останяем интервал по сохраненному ID
clearInterval(intervalRef.current);
};
useEffect(() => {
return () => clearInterval(intervalRef.current);
}, []);
return (
<div>
<p>Seconds: {seconds}</p>
<button onClick={startTimer}>Start</button>
<button onClick={stopTimer}>Stop</button>
</div>
);
}
3. Получение значения без создания зависимости в useEffect
// ❌ ПРОБЛЕМА: Бесконечный loop
function SearchComponent() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
useEffect(() => {
const search = async () => {
const res = await fetch(`/api/search?q=${query}`);
setResults(await res.json());
};
search();
}, [query]); // Каждое изменение query триггерит новый поиск
}
// ✅ РЕШЕНИЕ: useRef для отслеживания предыдущего значения
function SearchComponent() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const prevQueryRef = useRef('');
useEffect(() => {
// Ищем только если query действительно изменился
if (prevQueryRef.current !== query) {
prevQueryRef.current = query;
const search = async () => {
const res = await fetch(`/api/search?q=${query}`);
setResults(await res.json());
};
search();
}
}, [query]);
return (
<div>
<input
value={query}
onChange={e => setQuery(e.target.value)}
/>
<Results items={results} />
</div>
);
}
4. Сохранение предыдущего значения
// Пользовательский хук для отслеживания предыдущего значения
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
// Использование
function Counter() {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
return (
<div>
<p>Текущее значение: {count}</p>
<p>Предыдущее значение: {prevCount}</p>
<button onClick={() => setCount(count + 1)}>Увеличить</button>
</div>
);
}
5. Отправка фокуса на ошибки в формах
function ContactForm() {
const emailRef = useRef(null);
const messageRef = useRef(null);
const [errors, setErrors] = useState({});
const handleSubmit = (e) => {
e.preventDefault();
const newErrors = {};
// Валидация
if (!emailRef.current.value.includes('@')) {
newErrors.email = 'Invalid email';
}
if (messageRef.current.value.length < 10) {
newErrors.message = 'Message too short';
}
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
// Фокус на первое поле с ошибкой
if (newErrors.email) {
emailRef.current?.focus();
} else if (newErrors.message) {
messageRef.current?.focus();
}
return;
}
// Отправка формы
console.log('Form submitted');
};
return (
<form onSubmit={handleSubmit}>
<input
ref={emailRef}
type="email"
placeholder="Email"
/>
{errors.email && <span>{errors.email}</span>}
<textarea
ref={messageRef}
placeholder="Message"
/>
{errors.message && <span>{errors.message}</span>}
<button type="submit">Send</button>
</form>
);
}
Когда НЕ использовать useRef
// ❌ ПЛОХО — используй useState вместо useRef
function Counter() {
const countRef = useRef(0);
const increment = () => {
countRef.current++;
// UI НЕ обновится! Пользователь не увидит новое значение
};
return (
<div>
{countRef.current} {/* Всегда 0 */}
<button onClick={increment}>+</button>
</div>
);
}
// ✅ ПРАВИЛЬНО — используй useState
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1); // UI обновляется
};
return (
<div>
{count} {/* Обновляется */}
<button onClick={increment}>+</button>
</div>
);
}
Ключевые моменты
- useRef сохраняется между рендерами — значение не теряется
- Изменение ref НЕ триггерит re-render — используй только для значений, которые не должны обновлять UI
- useRef.current — так обращаешься к значению
- useRef === вспомогательный инструмент — в большинстве случаев нужен useState
- Избегай overuse — это не панацея, используй только когда действительно необходимо
useRef — это мощный инструмент для работы с DOM и сохранения значений, но его нужно использовать с умом. Чаще всего проблемы решаются через useState и useEffect.