← Назад к вопросам

Приведи примеры использования useRef

2.0 Middle🔥 211 комментариев
#React

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Использование 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

  1. Не обновляй 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>;
}
  1. Используй useRef для DOM доступа, а не для состояния — если нужно отслеживать значение и вызывать перерендер, используй useState.

  2. Ref не проходит через компоненты — используй forwardRef если нужно передать ref в дочерний компонент:

const TextInput = forwardRef((props, ref) => {
  return <input ref={ref} type="text" />;
});

Заключение

useRef — мощный инструмент для прямого управления DOM элементами и сохранения мутабельных значений. Основное правило: используй его для DOM доступа и хранения значений, которые не должны вызывать перерендер, а для состояния приложения используй useState.