useRef в React — работа с DOM элементами

useRef хранит изменяемое значение между рендерами, но изменение ref не запускает новый рендер. Чаще всего ref используют для доступа к DOM-элементу или хранения технического значения: id таймера, предыдущего значения, флага.

Синтаксис useRef

import { useRef } from 'react';

const inputRef = useRef(null);

Фокус на input

function SearchBox() {
  const inputRef = useRef(null);

  function focusInput() {
    inputRef.current.focus();
  }

  return (
    <div>
      <input ref={inputRef} placeholder="Поиск" />
      <button onClick={focusInput}>Фокус</button>
    </div>
  );
}

После рендера inputRef.current будет ссылкой на настоящий DOM-элемент input. До рендера там null.

useRef и useState

useStateuseRef
Изменение запускает рендерИзменение не запускает рендер
Используется для данных интерфейсаИспользуется для технических значений
Значение читается в JSXЗначение обычно не показывают напрямую

Хранить id таймера

function Stopwatch() {
  const [seconds, setSeconds] = useState(0);
  const intervalRef = useRef(null);

  function start() {
    if (intervalRef.current) return;
    intervalRef.current = setInterval(() => {
      setSeconds(s => s + 1);
    }, 1000);
  }

  function stop() {
    clearInterval(intervalRef.current);
    intervalRef.current = null;
  }

  return (
    <>
      <p>{seconds}</p>
      <button onClick={start}>Старт</button>
      <button onClick={stop}>Стоп</button>
    </>
  );
}

Чего не делать с ref

  • Не храните в ref данные, которые должны отображаться на экране.
  • Не читайте и не меняйте ref.current во время рендера без причины.
  • Не используйте ref как замену нормальному потоку данных через props и state.

Пример: предыдущее значение

useRef удобно хранит значение между рендерами. Например, можно показать пользователю предыдущее значение счётчика.

function PreviousCounter() {
  const [count, setCount] = useState(0);
  const prevCountRef = useRef(null);

  useEffect(() => {
    prevCountRef.current = count;
  }, [count]);

  return (
    <div>
      <p>Сейчас: {count}</p>
      <p>Было: {prevCountRef.current ?? 'нет значения'}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}

Изменение prevCountRef.current не запускает рендер. Рендер запускает setCount, а ref просто хранит информацию между этими рендерами.

Пример: измерить размер блока

function MeasureBox() {
  const boxRef = useRef(null);

  function showSize() {
    const rect = boxRef.current.getBoundingClientRect();
    alert(`Ширина: ${rect.width}px`);
  }

  return (
    <>
      <div ref={boxRef}>Измеряемый блок</div>
      <button onClick={showSize}>Показать размер</button>
    </>
  );
}

Главный ориентир

Если значение должно отображаться на экране, берите useState. Если значение нужно коду, но не должно само перерисовывать компонент, берите useRef.

Эта граница особенно важна в формах и таймерах. Если вы спрятали видимое значение в ref, интерфейс может отстать от данных. Если положили технический id в state, получите лишние рендеры без пользы.

Когда useRef лучше useState

useRef нужен для значений, которые должны пережить рендер, но не должны сами запускать новый рендер. Это техническое хранилище, а не место для данных интерфейса.

Типичный пример — id таймера, DOM-элемент, предыдущее значение или флаг, который нужен обработчику, но не должен менять разметку. Если же значение выводится в JSX, ref почти наверняка выбран неправильно.

Хорошие сценарии

  • сфокусировать input после клика;
  • хранить id таймера;
  • запомнить предыдущее значение;
  • измерить размер DOM-элемента;
  • держать флаг, который нужен обработчику, но не показывается на экране.

Плохой сценарий

Если значение должно отображаться пользователю, не прячьте его в ref. Пользователь увидит изменения только после какого-то другого рендера, и код станет непредсказуемым. Для видимых данных используйте useState.

Простая проверка

Изменение значения должно сразу менять экран? Берите useState. Значение нужно только коду и не должно перерисовывать компонент? Берите useRef.

Если ответ не очевиден, начните с useState и посмотрите, нужно ли значение пользователю на экране. В useRef переносите только то, что должно сохраниться между рендерами, но не участвует в отображении.

Оцените статью
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x