React DevTools: отладка и профилирование компонентов

10 сентября 2026
Автор

Антон Ларичев

Что такое React DevTools

React DevTools — официальное расширение для браузера и отдельный пакет, которое позволяет инспектировать дерево компонентов React, просматривать и редактировать состояние и пропсы в реальном времени, а также профилировать производительность рендеров.

Расширение доступно для Chrome, Firefox и Edge. Установить его можно из магазина расширений вашего браузера, найдя «React Developer Tools».

После установки в DevTools браузера появятся две новые вкладки:

  • Components — инспектор дерева компонентов
  • Profiler — инструмент профилирования рендеров

Курс по теме

Курс React и Redux Toolkit с нуля

40 000+ студентов · рейтинг 4.8 · гарантия возврата 30 дней

Вкладка Components

Навигация по дереву компонентов

Вкладка Components отображает иерархию React-компонентов текущей страницы. Вы можете:

  • Раскрывать и сворачивать узлы дерева
  • Искать компоненты по имени через поисковую строку
  • Кликать на компонент, чтобы увидеть его пропсы, состояние и контекст в правой панели

Пример простого приложения для демонстрации:

import { useState } from 'react';

function Counter({ label }: { label: string }) {
  const [count, setCount] = useState(0);

  return (
    <div>
      <span>{label}: {count}</span>
      <button onClick={() => setCount(c => c + 1)}>+</button>
    </div>
  );
}

export function App() {
  return (
    <div>
      <Counter label="Первый" />
      <Counter label="Второй" />
    </div>
  );
}

В дереве DevTools вы увидите App, внутри него два экземпляра Counter. При выборе любого из них правая панель покажет текущее значение count и проп label.

Инспекция и редактирование состояния

Правая панель вкладки Components разделена на несколько секций:

props — пропсы, переданные компоненту. Значения можно редактировать прямо в DevTools (двойной клик на значении), что мгновенно обновит рендер.

hooks — список хуков компонента с их текущими значениями. Для useState отображается текущее состояние, для useReducer — состояние и диспетчер, для useContext — текущее значение контекста.

rendered by — цепочка компонентов, породивших данный компонент. Помогает понять, откуда пришёл компонент в глубоко вложенных деревьях.

Поиск и фильтрация

В строке поиска можно вводить имя компонента или его часть. DevTools поддерживает поиск по регулярным выражениям — достаточно включить иконку /.*$/.

В настройках (иконка шестерёнки) можно скрыть хост-элементы (<div>, <span> и прочие DOM-узлы), чтобы видеть только React-компоненты, или отфильтровать компоненты по имени.

Выбор элемента на странице

Иконка курсора в левом верхнем углу вкладки Components позволяет кликнуть на любой элемент страницы — DevTools автоматически выберет соответствующий React-компонент в дереве. Это удобно, когда нужно быстро найти компонент, не зная его имени.

Консольный доступ к компоненту

Выбрав компонент во вкладке Components, можно перейти в консоль браузера и обратиться к нему через специальную переменную:

// $r — ссылка на последний выбранный компонент в React DevTools
console.log($r);
// Для DOM-узлов — аналог $0 в Elements

Переменная $r аналогична $0 на вкладке Elements — она всегда указывает на последний выбранный компонент.

Отладка хуков

useState и useReducer

DevTools отображает текущее значение состояния рядом с именем хука. Для useState это выглядит так:

hooks
  State: 0           ← текущее значение useState

Для useReducer:

hooks
  Reducer: { count: 0, status: 'idle' }   ← текущий объект состояния

Обоим можно редактировать значение прямо в DevTools — двойной клик открывает поле ввода.

useContext

Контекст отображается в секции hooks как:

hooks
  Context: { theme: 'dark', lang: 'ru' }

Пример компонента с контекстом:

import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext<'light' | 'dark'>('light');

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>Кнопка</button>;
}

export function App() {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');

  return (
    <ThemeContext.Provider value={theme}>
      <ThemedButton />
      <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
        Переключить тему
      </button>
    </ThemeContext.Provider>
  );
}

В DevTools для ThemedButton в секции hooks будет отображено текущее значение контекста и подсвечен Provider, откуда оно пришло.

Пользовательские хуки

DevTools группирует хуки пользовательского хука под его именем:

function useWindowSize() {
  const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });

  useEffect(() => {
    const handler = () => setSize({ width: window.innerWidth, height: window.innerHeight });
    window.addEventListener('resize', handler);
    return () => window.removeEventListener('resize', handler);
  }, []);

  return size;
}

function Layout() {
  const size = useWindowSize();
  return <div>{size.width}x{size.height}</div>;
}

В DevTools вы увидите:

hooks
  WindowSize
    State: { width: 1440, height: 900 }
    Effect

Вкладка Profiler

Как начать запись

Вкладка Profiler позволяет записывать сессию взаимодействия с приложением и анализировать, какие компоненты рендерились, сколько раз и как долго.

  1. Откройте вкладку Profiler
  2. Нажмите кнопку записи (круглая иконка)
  3. Выполните действия в приложении, которые хотите проанализировать
  4. Нажмите «Stop» для завершения записи

Flamegraph — граф пламени

После записи отображается flamegraph — визуализация каждого коммита рендера. По горизонтали расположено время, по вертикали — глубина дерева компонентов.

Цвета блоков:

  • Серый — компонент не рендерился в этом коммите
  • Голубой (холодный) — компонент рендерился быстро
  • Жёлтый/оранжевый — компонент рендерился дольше
  • Красный — компонент рендерился медленнее всего

Клик на блок компонента показывает, почему он рендерился: изменились пропсы, состояние или хук.

Ranked chart — ранжированный список

Вид «Ranked» показывает все компоненты текущего коммита, отсортированные по времени рендера от медленного к быстрому. Это удобно для быстрого поиска самых затратных компонентов.

Анализ конкретного коммита

Каждое изменение состояния, вызвавшее ре-рендер, создаёт отдельный «коммит» на временной шкале. Навигационные стрелки позволяют переключаться между коммитами и видеть состояние дерева компонентов на каждом из них.

Пример ситуации, которую легко поймать через Profiler:

import { useState } from 'react';

// Этот компонент будет ре-рендериться каждый раз при изменении count,
// даже если его пропсы не менялись
function ExpensiveChild({ name }: { name: string }) {
  // Имитация тяжёлых вычислений
  const result = Array.from({ length: 10000 }, (_, i) => i).reduce((a, b) => a + b, 0);
  return <div>{name}: {result}</div>;
}

export function App() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
      <ExpensiveChild name="Дорогой компонент" />
    </div>
  );
}

В Profiler при каждом клике кнопки будет видно, что ExpensiveChild рендерится с пометкой «Props did not change» — это сигнал обернуть его в React.memo.

Решение: React.memo

import { useState, memo } from 'react';

const ExpensiveChild = memo(function ExpensiveChild({ name }: { name: string }) {
  const result = Array.from({ length: 10000 }, (_, i) => i).reduce((a, b) => a + b, 0);
  return <div>{name}: {result}</div>;
});

export function App() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
      <ExpensiveChild name="Дорогой компонент" />
    </div>
  );
}

Теперь в Profiler ExpensiveChild будет серым (не рендерился) при изменении count.

Настройки Profiler

Запись причин рендера

В настройках DevTools (иконка шестерёнки на вкладке Profiler) включите опцию «Record why each component rendered while profiling». После этого для каждого компонента в текущем коммите будет отображаться конкретная причина ре-рендера:

  • State changed — изменилось состояние
  • Props changed — изменились пропсы (с указанием, какие именно)
  • Hooks changed — изменился результат хука
  • Context changed — изменился контекст
  • The parent component rendered — родитель перерендерился

Timeline-профилирование (React 18+)

В React 18 и новее Profiler поддерживает временну́ю шкалу с разметкой задач планировщика (Scheduler). Это позволяет видеть, как React распределяет работу между фреймами анимации, и находить места, где долгий рендер блокирует основной поток.

Практические сценарии отладки

Поиск лишних ре-рендеров

Алгоритм:

  1. Запустить запись в Profiler
  2. Выполнить одно конкретное действие (например, нажать кнопку)
  3. Остановить запись
  4. Во вкладке Ranked найти компоненты, которые отрендерились, хотя не должны были
  5. Проверить причину рендера — если «The parent component rendered» и пропсы не менялись, рассмотреть React.memo, useMemo или useCallback

Отладка useCallback и useMemo

import { useState, useCallback, memo } from 'react';

const Child = memo(function Child({ onClick }: { onClick: () => void }) {
  console.log('Child rendered');
  return <button onClick={onClick}>Нажми</button>;
});

export function App() {
  const [count, setCount] = useState(0);
  const [other, setOther] = useState(0);

  // Без useCallback Child будет рендериться при каждом изменении other
  const handleClick = useCallback(() => {
    setCount(c => c + 1);
  }, []); // пустой массив зависимостей — функция создаётся один раз

  return (
    <div>
      <Child onClick={handleClick} />
      <button onClick={() => setOther(o => o + 1)}>Other: {other}</button>
      <span>Count: {count}</span>
    </div>
  );
}

В Profiler при нажатии «Other» компонент Child должен оставаться серым — это подтверждает, что useCallback работает корректно.

Поиск медленных вычислений

Если компонент регулярно попадает в красную зону Profiler, причиной может быть тяжёлое вычисление при рендере:

import { useState, useMemo } from 'react';

function FibList({ n }: { n: number }) {
  // Без useMemo это вычисление будет выполняться при каждом ре-рендере
  const fibs = useMemo(() => {
    const result = [0, 1];
    for (let i = 2; i <= n; i++) {
      result.push(result[i - 1] + result[i - 2]);
    }
    return result;
  }, [n]); // пересчитывается только при изменении n

  return <ul>{fibs.map((f, i) => <li key={i}>{f}</li>)}</ul>;
}

После добавления useMemo в Profiler время рендера FibList резко сократится при рендерах, не связанных с изменением n.

DevTools в production-сборке

По умолчанию React DevTools работают только с development-сборкой. В production-сборке многие функции недоступны — это сделано намеренно для защиты данных и производительности.

Если нужно профилировать production-сборку, используйте специальный profiling-билд:

npm install react-dom/profiling

Или настройте алиас в webpack/vite:

// vite.config.ts
export default {
  resolve: {
    alias: {
      'react-dom': 'react-dom/profiling',
    },
  },
};

Полезные клавиатурные сокращения

Действие Сочетание
Поиск компонента Ctrl/Cmd + F на вкладке Components
Навигация по результатам поиска Enter / Shift + Enter
Переход к DOM-узлу Иконка <> в правой панели
Копировать значение пропа Правый клик → Copy value

Итог

React DevTools — незаменимый инструмент в арсенале React-разработчика. Вкладка Components позволяет быстро инспектировать и модифицировать состояние без изменения кода, а вкладка Profiler даёт точные данные о том, какие компоненты замедляют приложение и почему. Регулярный анализ через Profiler в связке с React.memo, useMemo и useCallback — основа для построения производительных React-приложений.

Чтобы глубже освоить React и научиться строить быстрые приложения с нуля, приходите на курс React на PurpleSchool.

Стрелочка влевоReact PWA: Progressive Web App с ReactReact Compiler: автоматическая мемоизацияСтрелочка вправо

Постройте личный план изучения React до уровня Middle — бесплатно!

React — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по React

React тестирование с Vitest
React с TanStack Router
Uncontrolled Components: когда DOM управляет даннымиБезопасность в React: защита от XSS, CSRF и утечек данныхRender Props: гибкое управление рендерингом в ReactРефакторинг React-кода: техники и лучшие практикиReact с WebSocket — реалтайм в компонентахReact Native приложение с нуля до публикации в сторахПрофилирование React: как найти и устранить узкие местаЧастичное применение: как создавать компоненты без лишнего кодаИменование компонентов в React: соглашения и лучшие практикиЛенивая загрузка: как ускорить React-приложение в разыОшибки гидратации в React и способы их устраненияHOC в React: мастерство композиции компонентовuseMemo: как спасти производительность от тяжелых вычисленийError Boundaries: создаем надежные React-приложенияКонтролируемые компоненты в React: полный контроль над формамиCompound Components в React: создаем гибкие компоненты с мощным APIДокументирование компонентов в React: Storybook, JSDoc и READMEКомпозиция компонентов в React: строим гибкие интерфейсыКомментирование кода в React: когда и как писать комментарииCode Splitting в React: как уменьшить бандл и ускорить загрузку приложенияАсинхронные компоненты в React: новый стандарт работы с даннымиДоступность (a11y) в React: ARIA, семантика и клавиатурная навигация
Zustand — управление состоянием в ReactZod - валидация с TypeScriptYup - валидация схемТемизация в ReactXState - конечные автоматыТестирование хуковTailwind CSS с ReactSWR - библиотека для запросовStyled Components — стилизация через JSStorybook - документация компонентовSnapshots тестированиеRTK Query - работа с APIRedux Toolkit - современный ReduxRecoil — библиотека управления состоянием от FacebookВиртуализация списков с react-window: как отображать тысячи элементов без лаговReact Toastify - уведомления в ReactReact Testing LibraryСоздание таблиц в React гайд по react-tableReact Spring - анимацииРабота с формами и селектами в ReactReact Query (TanStack Query) - работа с серверомПлагины в React что это и как их использоватьReact PDF - работа с PDF файламиОбзор популярных библиотек для ReactReact Icons - библиотека иконок для ReactReact Hook Form — валидация форм в ReactReact Dropzone — загрузка файловReact Beautiful DnD - перетаскивание элементовАнимация при монтировании компонентов в ReactМокирование APIПодключение Bootstrap к React-приложениюMobX — реактивное управление состоянием в ReactМикрофронтенды с React (micro-frontends)Загрузка и индикаторыАнимация списков в ReactJotai - атомарное состояниеБесконечная прокруткаFramer Motion - библиотека анимацийEmotion — библиотека CSS-in-JSДинамические стили в ReactE2E тестирование с CypressCSSTransition - переходыCSS-in-JS — плюсы и минусыКонтекст vs Redux — когда что использоватьИспользование Chart.js в ReactAxios с ReactТестирование асинхронных компонентовОбработка ошибок API
useState в React что это и как использоватьuseTransition - плавные переходы между состояниямиuseSyncExternalStore — работа с внешними сторамиuseRef в React — создание ссылок на DOM и значенияuseOptimistic — оптимистичные обновления UIuseLayoutEffect в React — эффект до отрисовкиuseInsertionEffect — внедрение стилей до мутаций DOMuseImperativeHandle в React — настройка ref дочернего компонентаuseId — генерация уникальных идентификаторовuseFormStatus - отслеживание статуса отправки формыuseDeferredValue — отложенное обновление состоянияuseDebugValue — отладка кастомных хуковuseCallback в React — мемоизация функцийuseReducer — альтернатива useState для сложной логикиuseMemo в React: как и когда оптимизировать тяжелые вычисленияХук use() в React 19useEffect в React что это и как использоватьuseContext — работа с контекстом в ReactuseCallback в React — мемоизация функций и оптимизация ре-рендеровuseActionState в React 19Оптимизация рендеринга в React: от теории к глубокой практикеЧто такое useRef и как его применять в ReactКак и зачем использовать React HooksУправление состоянием в React через ContextНовые возможности React 19Как предотвратить лишние ре-рендеры в React: полное руководствоuseMemo vs useCallback: подробное руководство по мемоизации в ReactПравила хуков — правила использованияflushSync — синхронные обновления состояния в ReactuseEffect vs useLayoutEffect: в чём разница и какой хук выбрать?Кастомные хуки в React — создание собственных хуковuseState продвинутое использование в React
Transition API — плавные обновления интерфейса в ReactReact Suspense — приостановка рендераStrictMode в React — как находить ошибки на этапе разработкиСерверные компоненты React (RSC) — подробный разбор и практикаКак работает рендеринг в ReactЧто такое props в React и как их правильно использоватьКак работает JSX связка React и HTMLЧто такое React.js и как его использоватьКак использовать элементы в ReactКак использовать React DOM в проектеЧто такое компоненты в React и как их применятьРабота с children в ReactПорталы в React: рендер компонентов вне иерархии DOMFragment в React: группировка элементов без лишних узлов DOMCSS Modules в ReactConcurrent Mode — конкурентный режим в React
Открыть базу знаний

Лучшие курсы по теме

изображение курса

React и Redux Toolkit

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий