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

Перерисуется ли компонент, если записать примитив из UseRef в зависимости UseEffect

2.2 Middle🔥 231 комментариев
#React

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

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

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

Влияние записи в ref на триггер эффекта

Короткий ответ: Нет, компонент не перерисуется, если вы запишете примитив в useRef внутри зависимости useEffect. Более того, такая запись не вызовет даже повторного выполнения самого эффекта.

Подробное объяснение

Как работают ref и зависимости useEffect

useRef создает мутируемый объект, свойство .current которого можно изменять без триггера ререндера. Ref сохраняет свое значение между рендерами, но его изменение не считается "изменением состояния" с точки зрения реактивности React.

Массив зависимостей useEffect определяет, когда эффект должен выполняться повторно. React использует поверхностное сравнение (shallow comparison) предыдущих и текущих значений зависимостей.

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

function ExampleComponent() {
  const [count, setCount] = useState(0);
  const myRef = useRef(0);
  
  useEffect(() => {
    console.log('Эффект выполнился, count:', count);
    // Меняем значение ref
    myRef.current = 100;
  }, [myRef.current]); // Зависимость от примитива в ref

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Увеличить count (вызовет ререндер)
      </button>
      <p>Ref value: {myRef.current}</p>
    </div>
  );
}

Почему не происходит ререндер

  1. Ref не триггерит ререндеры

    • Изменение myRef.current происходит синхронно и не планирует обновление компонента
    • React не отслеживает изменения ref как изменения состояния
  2. Поверхностное сравнение зависимостей

    • При каждом рендере React сравнивает предыдущее_значение и текущее_значение для каждой зависимости
    • Для примитивов (числа, строки, булевы) используется строгое равенство ===
    • В примере выше: prevDeps[0] === currentDeps[0] всегда будет true, если значение не менялось между рендерами

Ключевая проблема: стабильность ссылки vs значение

Самая важная деталь: ссылка на объект ref (myRef) стабильна между рендерами, но значение myRef.current может меняться. Однако:

// Между рендерами объект myRef остается тем же
console.log(prevMyRef === currentMyRef); // true

// Но значение внутри может отличаться
console.log(prevMyRef.current === currentMyRef.current); // может быть false

Практический пример и выводы

function ProblematicComponent() {
  const ref = useRef(0);
  const [state, setState] = useState(0);
  
  // ❌ ПЛОХО: бесконечный цикл не возникнет, но эффект выполнится только один раз
  useEffect(() => {
    console.log('Эффект выполнен');
    ref.current = Date.now(); // Меняем значение ref
  }, [ref.current]); // Зависимость от значения ref
  
  // ✅ ПРАВИЛЬНО: если нужно реагировать на изменения ref
  useEffect(() => {
    // Этот эффект не зависит от значения ref
    console.log('State изменился:', state);
  }, [state]);

  return (
    <button onClick={() => setState(state + 1)}>
      Рендер: {state}, Ref: {ref.current}
    </button>
  );
}

Основные выводы

  1. Изменение ref.current не вызывает ререндер компонента
  2. Эффект с зависимостью [ref.current] выполнится только один раз при монтировании, так как React видит, что зависимость (число) не изменилась между рендерами
  3. Ref предназначены для хранения мутируемых значений, которые не должны влиять на рендеринг
  4. Если вам нужно реагировать на изменение значения - используйте состояние (useState), а не ref
  5. Единственный случай, когда ref в зависимостях имеет смысл - это когда вы используете сам объект ref (не его значение):
// ✅ Имеет смысл: эффект зависит от стабильности ref объекта
const ref = useRef();
useEffect(() => {
  // Работа с DOM элементом
  if (ref.current) {
    ref.current.focus();
  }
}, [ref]); // ref объект стабилен, эффект выполнится один раз

Золотое правило: Если значение должно влиять на рендер или триггерить эффекты - используйте состояние. Если значение должно сохраняться между рендерами без влияния на них - используйте ref.

Перерисуется ли компонент, если записать примитив из UseRef в зависимости UseEffect | PrepBro