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

Существуют ли способы положить что-либо в useRef помимо ссылки на узел

2.0 Middle🔥 212 комментариев
#JavaScript Core

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

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

Да, абсолютно. Помимо хранения ссылок на DOM-узлы, useRef — это универсальный контейнер для мутируемого значения, которое должно сохраняться между рендерами, не вызывая при этом новый рендер при его изменении. Это его второе, но не менее важное предназначение.

Основная идея: useRef возвращает изменяемый (мутабельный) объект, свойство .current которого инициализируется переданным аргументом и сохраняется на протяжении всего времени жизни компонента. Изменение этого объекта не приводит к ререндеру компонента.

Вот основные сценарии использования useRef не для DOM:

1. Хранение мутабельных переменных

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

import { useRef, useEffect } from 'react';

function TimerComponent() {
  // Храним ID таймера, чтобы очистить его при размонтировании
  const timerIdRef = useRef(null);

  useEffect(() => {
    timerIdRef.current = setInterval(() => {
      console.log('Тик');
    }, 1000);

    // Очистка при размонтировании
    return () => {
      clearInterval(timerIdRef.current);
    };
  }, []);

  // timerIdRef.current можно читать и изменять в любом месте
}

2. Хранение предыдущего значения пропса или состояния

Классический паттерн, когда нужно сравнить текущее значение с предыдущим.

import { useRef, useEffect } from 'react';

function ValueTracker({ value }) {
  const prevValueRef = useRef();

  useEffect(() => {
    prevValueRef.current = value;
  }); // Без массива зависимостей - выполняется после каждого рендера

  const prevValue = prevValueRef.current;

  return (
    <div>
      Текущее: {value}, Предыдущее: {prevValue !== undefined ? prevValue : 'N/A'}
    </div>
  );
}

3. Флаги и статусы для эффектов

Например, чтобы предотвратить выполнение логики при первом монтировании или отслеживать, монтирован ли еще компонент (для асинхронных операций).

import { useRef, useEffect, useState } from 'react';

function DataFetcher({ id }) {
  const [data, setData] = useState(null);
  // Флаг, чтобы избежать установки состояния на размонтированном компоненте
  const isMountedRef = useRef(true);

  useEffect(() => {
    return () => {
      isMountedRef.current = false;
    };
  }, []);

  useEffect(() => {
    fetchData(id).then(fetchedData => {
      // Проверяем, жив ли еще компонент, перед обновлением состояния
      if (isMountedRef.current) {
        setData(fetchedData);
      }
    });
  }, [id]);

  // Аналогично можно хранить флаг "первый рендер"
  const isFirstRenderRef = useRef(true);
  useEffect(() => {
    if (isFirstRenderRef.current) {
      isFirstRenderRef.current = false;
    } else {
      // Логика, которая не должна выполняться при первом рендере
    }
  });
}

4. Кэширование вычислений или данных

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

import { useRef } from 'react';
import { HeavyCalculator } from './some-library';

function CalculatorComponent({ input }) {
  // Экземпляр тяжелого класса создается один раз
  const calculatorInstanceRef = useRef(new HeavyCalculator());

  // Или кэширование результата функции
  const cacheRef = useRef({});
  const calculateSomething = (key) => {
    if (!cacheRef.current[key]) {
      cacheRef.current[key] = performExpensiveCalculation(key);
    }
    return cacheRef.current[key];
  };

  const result = calculateSomething(input);
  // ...
}

5. Хранение ссылок на функции (для актуальности в колбэках)

Позволяет всегда иметь доступ к самой последней версии функции, даже внутри useEffect или других колбэков с фиксированными зависимостями.

import { useRef, useCallback } from 'react';

function Form() {
  const [value, setValue] = useState('');
  // Сохраняем актуальный обработчик в ref
  const handleSubmitRef = useRef(() => {
    console.log('Текущее значение для отправки:', value);
  });

  // Обновляем ref при каждом изменении value
  useEffect(() => {
    handleSubmitRef.current = () => {
      console.log('Текущее значение для отправки:', value);
      // Здесь могла бы быть отправка формы
    };
  }, [value]);

  // Стабильный колбэк, который всегда вызывает актуальную функцию
  const stableSubmitHandler = useCallback(() => {
    handleSubmitRef.current();
  }, []); // Зависимости пусты, колбэк не меняется!

  return <button onClick={stableSubmitHandler}>Отправить</button>;
}

Важные отличия от состояния (useState)

АспектuseRefuseState
Триггер рендера❌ Изменение .current не вызывает ререндер.✅ Изменение состояния вызывает ререндер.
ИммутабельностьМутабельный. Можно напрямую изменять .current.Иммутабельный. Изменение только через сеттер.
СинхронностьИзменения .current синхронны и мгновенны.Обновления состояния могут быть асинхронными и батчироваться.
Основное назначениеХранение "теневых" данных, DOM-ссылок, экземпляров.Хранение данных, от которых зависит отображение UI.

Заключение

useRef — это не просто "крючок для DOM". Это фундаментальный инструмент для работы с мутабельными данными в рамках парадигмы иммутабельности и реактивности React. Он служит мостом между реактивным миром пропсов, состояния и рендеров и императивным миром таймеров, инстансов классов, флагов и кэшей. Понимание этого двойного назначения критически важно для написания эффективных и корректных React-приложений.

Существуют ли способы положить что-либо в useRef помимо ссылки на узел | PrepBro