Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Использование useRef
useRef — это хук React, который позволяет получить прямой доступ к DOM элементам или сохранить мутабельное значение, которое не вызывает переренdering. Ref возвращает объект с единственным свойством current, значение которого персистирует между перерендерами.
Основное отличие от useState
// useState вызывает перерендер
const [count, setCount] = useState(0);
setCount(count + 1); // Компонент перерендерится
// useRef НЕ вызывает перерендер
const countRef = useRef(0);
countRef.current++; // Компонент НЕ перерендерится
Пример 1: Доступ к DOM элементу
Частое использование — получить доступ к DOM элементу для управления им:
import { useRef } from "react";
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} type="text" placeholder="Введи текст" />
<button onClick={focusInput}>Фокус на input</button>
</>
);
}
Когда нажимаешь кнопку, фокус автоматически перемещается на input, и пользователь может сразу писать.
Пример 2: Управление видео/аудио плеером
function VideoPlayer() {
const videoRef = useRef(null);
const handlePlay = () => {
videoRef.current.play();
};
const handlePause = () => {
videoRef.current.pause();
};
const handleSeek = (seconds) => {
videoRef.current.currentTime = seconds;
};
return (
<div>
<video ref={videoRef} width="400" controls>
<source src="video.mp4" type="video/mp4" />
</video>
<div style={{ marginTop: "10px" }}>
<button onClick={handlePlay}>Воспроизведение</button>
<button onClick={handlePause}>Пауза</button>
<button onClick={() => handleSeek(10)}>Перейти на 10 сек</button>
</div>
</div>
);
}
Пример 3: Сохранение мутабельного значения
Это полезно для сохранения значений, которые не должны вызывать перерендер:
function Timer() {
const intervalRef = useRef(null);
const [count, setCount] = useState(0);
const startTimer = () => {
intervalRef.current = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
};
const stopTimer = () => {
clearInterval(intervalRef.current);
};
return (
<div>
<p>Секунды: {count}</p>
<button onClick={startTimer}>Старт</button>
<button onClick={stopTimer}>Стоп</button>
</div>
);
}
Мы сохраняем ID интервала в intervalRef.current, чтобы позже его отменить. Это значение не нужно отслеживать как состояние.
Пример 4: Отслеживание предыдущего значения
function Component() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
// Сохраняем текущее значение в ref ПОСЛЕ рендера
prevCountRef.current = count;
}, [count]);
return (
<div>
<p>Текущее: {count}</p>
<p>Предыдущее: {prevCountRef.current}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
Пример 5: Счетчик кликов без перерендера (редкое использование)
function ClickCounter() {
const clickCountRef = useRef(0);
const [, setUpdateCount] = useState(0);
const handleClick = () => {
clickCountRef.current++;
// Компонент НЕ перерендерится здесь
};
const showCount = () => {
// Принудительный перерендер чтобы показать значение
setUpdateCount(prev => prev + 1);
alert(`Кликов: ${clickCountRef.current}`);
};
return (
<>
<button onClick={handleClick}>Кликни (в ref)</button>
<button onClick={showCount}>Показать количество</button>
</>
);
}
Пример 6: Работа с модальными окнами
function Modal() {
const dialogRef = useRef(null);
const openModal = () => {
dialogRef.current?.showModal();
};
const closeModal = () => {
dialogRef.current?.close();
};
return (
<>
<button onClick={openModal}>Открыть диалог</button>
<dialog ref={dialogRef}>
<h2>Модальное окно</h2>
<p>Это содержимое модального окна</p>
<button onClick={closeModal}>Закрыть</button>
</dialog>
</>
);
}
Пример 7: Интеграция с библиотеками
import Map from "leaflet";
function MapComponent() {
const mapRef = useRef(null);
useEffect(() => {
// Инициализируем карту после монтирования компонента
if (mapRef.current) {
const map = new Map(mapRef.current);
map.setView([51.505, -0.09], 13);
}
}, []);
return <div ref={mapRef} style={{ height: "400px" }} />;
}
Пример 8: Отмена fetch запросов
function DataFetcher() {
const abortControllerRef = useRef(null);
const [data, setData] = useState(null);
const fetchData = async () => {
abortControllerRef.current = new AbortController();
try {
const response = await fetch("/api/data", {
signal: abortControllerRef.current.signal
});
const result = await response.json();
setData(result);
} catch (error) {
if (error.name !== "AbortError") {
console.error(error);
}
}
};
const cancelFetch = () => {
abortControllerRef.current?.abort();
};
useEffect(() => {
return () => {
// Отменяем запрос при размонтировании
cancelFetch();
};
}, []);
return (
<>
<button onClick={fetchData}>Загрузить данные</button>
<button onClick={cancelFetch}>Отменить</button>
{data && <div>{JSON.stringify(data)}</div>}
</>
);
}
Пример 9: Форма с мультиэлементами
function Form() {
const nameRef = useRef(null);
const emailRef = useRef(null);
const passwordRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
const formData = {
name: nameRef.current.value,
email: emailRef.current.value,
password: passwordRef.current.value
};
console.log(formData);
// Отправляем на сервер
};
return (
<form onSubmit={handleSubmit}>
<input ref={nameRef} type="text" placeholder="Имя" required />
<input ref={emailRef} type="email" placeholder="Email" required />
<input ref={passwordRef} type="password" placeholder="Пароль" required />
<button type="submit">Отправить</button>
</form>
);
}
Важные правила использования useRef
- Не обновляй ref в render-функции — это может привести к непредсказуемому поведению:
// Плохо
function BadComponent() {
const countRef = useRef(0);
countRef.current++; // Это выполнится на каждом рендере!
return <div>{countRef.current}</div>;
}
// Хорошо
function GoodComponent() {
const countRef = useRef(0);
useEffect(() => {
countRef.current++; // Только после рендера
}, []);
return <div>{countRef.current}</div>;
}
-
Используй useRef для DOM доступа, а не для состояния — если нужно отслеживать значение и вызывать перерендер, используй useState.
-
Ref не проходит через компоненты — используй forwardRef если нужно передать ref в дочерний компонент:
const TextInput = forwardRef((props, ref) => {
return <input ref={ref} type="text" />;
});
Заключение
useRef — мощный инструмент для прямого управления DOM элементами и сохранения мутабельных значений. Основное правило: используй его для DOM доступа и хранения значений, которые не должны вызывать перерендер, а для состояния приложения используй useState.