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

Что будешь проверять в коде при лишних перерендерах?

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

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

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

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

Стратегия анализа лишних перерендеров в React-приложениях

При оптимизации производительности React-приложений я систематически проверяю несколько ключевых аспектов, которые чаще всего становятся причинами избыточных ререндеров.

1. Анализ пропсов и их изменчивости

Первым делом проверяю пропсы, передаваемые компонентам:

// Проблемный код: объект создается при каждом рендере
function ParentComponent() {
  return <ChildComponent config={{ theme: 'dark', size: 'large' }} />;
}

// Оптимизированная версия: использование useMemo или вынос за пределы компонента
const CONFIG = { theme: 'dark', size: 'large' };

function ParentComponent() {
  const memoizedConfig = useMemo(() => ({ theme: 'dark', size: 'large' }), []);
  return <ChildComponent config={CONFIG} />;
}

Критически важно проверять:

  • Ссылочную целостность объектов и массивов (новые ссылки при каждом рендере)
  • Функции, передаваемые как пропсы (следует использовать useCallback)
  • Примитивные значения, которые могут неоправданно меняться

2. Проверка хуков состояния и контекста

Исследую использование useState, useReducer и контекста:

// Проблема: изменение состояния приводит к ререндеру всего крупного компонента
const [state, setState] = useState(initialState);

// Решение: разделение на мелкие компоненты или использование useMemo/useCallback
const heavyComponent = useMemo(() => <HeavyComponent />, [dependencies]);

Особое внимание уделяю контексту - изменения в провайдере контекста приводят к ререндерам всех потребителей, даже если они используют лишь часть значений.

3. Анализ зависимостей в хуках

Скрупулезно проверяю массивы зависимостей в useEffect, useMemo, useCallback:

// Неправильно: отсутствие зависимостей при их необходимости
const fetchData = useCallback(() => {
  // использует внешние переменные
}, []); // ← зависимости не указаны

// Правильно: полный список зависимостей
const fetchData = useCallback(() => {
  // логика с использованием userId
}, [userId]); // ← все зависимости указаны

4. Структура компонентов и мемоизация

Проверяю архитектуру компонентов на предмет:

  • Чрезмерного поднятия состояния (lifting state up)
  • Отсутствия разбиения на мелкие компоненты
  • Неправильного использования React.memo
// Неоптимально: крупный компонент с часто меняющейся частью
function UserProfile({ user, theme }) {
  // Много логики и разметки
  return (
    <div className={theme}>
      <Header />
      <UserDetails user={user} /> {/* Только эта часть зависит от user */}
      <Footer />
    </div>
  );
}

// Оптимизировано: выделение изменяемой части в отдельный компонент
const MemoizedUserDetails = React.memo(UserDetails);

function UserProfile({ user, theme }) {
  return (
    <div className={theme}>
      <Header />
      <MemoizedUserDetails user={user} />
      <Footer />
    </div>
  );
}

5. Использование инструментов для диагностики

Применяю специализированные инструменты:

  • React DevTools Profiler для записи и анализа рендеров
  • Why Did You Render для получения подробных предупреждений
  • Собственные хуки для отслеживания рендеров:
function useRenderCounter(componentName) {
  const renderCount = useRef(0);
  
  useEffect(() => {
    renderCount.current += 1;
    console.log(`${componentName} rendered: ${renderCount.current} times`);
  });
}

6. Проверка сторонних библиотек и интеграций

Уделяю внимание:

  • Библиотекам управления состоянием (Redux, MobX) и их конфигурации
  • Сторонним компонентам, которые могут вызывать лишние рендеры
  • Интеграциям с не-React кодом (нативные DOM-манипуляции, Web Workers)

7. Ключевые метрики и антипаттерны

Ищу распространенные антипаттерны:

  • Рендер-пропсы и дети как функции без мемоизации
  • Условные хуки или хуки в циклах
  • Избыточная подписка на события
  • Синхронные операции в рендере, блокирующие основной поток

Заключение

Мой подход к проверке лишних перерендеров систематичен: начинаю с анализа пропсов и состояния, перехожу к архитектуре компонентов, а затем к оптимизациям с помощью хуков мемоизации. Важно помнить, что преждевременная оптимизация может навредить - сначала измеряю производительность с помощью профилировщика, затем оптимизирую только проблемные места. Современные React-приложения с Concurrent Features требуют особого внимания к асинхронным рендерам и приоритизации обновлений, что добавляет новые аспекты для проверки при оптимизации ререндеров.