Урок 11

Отладка состояния

Когда состояние ведёт себя странно — нужны инструменты и подходы.

React DevTools

Расширение для Chrome и Firefox. Показывает:

  • Дерево компонентов
  • Пропсы и стейт каждого компонента
  • Кто кого рендерит
  • Время рендера

Установка: React Developer Tools из магазина расширений.

Компоненты и хуки

В DevTools можно выделить компонент и увидеть его состояние. Для хуков — вкладка «Hooks».

console.log в рендере

Простой способ увидеть, сколько раз рендерится компонент:

function Component() {
    console.log('render');
    // ...
}

Если рендеров слишком много — ищи причину.

useEffect с логом

useEffect(() => {
    console.log('state changed', state);
}, [state]);

Покажет, какие значения и когда менялись.

Почему компонент перерисовывается

React DevTools → Profiler → запись → взаимодействие. Покажет, какие компоненты рендерились и почему.

Бесконечный цикл

Признак: приложение зависает, в консоли сотни логов.

Причины:

  • setState в теле компонента без условий
  • Объект или функция в зависимостях useEffect
  • useEffect без зависимостей, который меняет стейт
// Бесконечный цикл
useEffect(() => {
    setCount(count + 1);
}, [count]);

Устаревшее значение

Если в замыкании старое значение — используй функциональную форму или useRef.

// Плохо
useEffect(() => {
    const id = setInterval(() => setCount(count + 1), 1000);
    return () => clearInterval(id);
}, []);

// Хорошо
useEffect(() => {
    const id = setInterval(() => setCount((c) => c + 1), 1000);
    return () => clearInterval(id);
}, []);

Мутация стейта

Если UI не обновляется — проверь, не мутируешь ли стейт.

// Не сработает
items.push('new');
setItems(items);

// Сработает
setItems([...items, 'new']);

StrictMode

В dev-режиме React монтирует компоненты дважды. Эффекты выполняются дважды. Это нормально. Если что-то ломается — проверь очистку.

useEffect(() => {
    const id = setInterval(...);
    return () => clearInterval(id); // обязательно
}, []);

Логирование в dev

Полезно логировать переходы состояния в редьюсере:

function reducer(state: State, action: Action) {
    console.log('action', action, 'state', state);
    switch (action.type) {
        // ...
    }
}

Redux DevTools

Если используешь Redux — отдельное расширение. Показывает все действия и состояние после каждого.

Для Zustand есть middleware devtools:

import { devtools } from 'zustand/middleware';

export const useStore = create(devtools((set) => ({
    // ...
})));

React Profiler

import { Profiler } from 'react';

<Profiler id="App" onRender={(id, phase, actualDuration) => {
    console.log({ id, phase, actualDuration });
}}>
    <App />
</Profiler>

Покажет время рендера компонентов.

Причины лишних рендеров

  • Родитель рендерится → дети тоже
  • Новый объект или функция в пропсах
  • Контекст изменился
  • Родитель передал новый key

Решение: React.memo, useMemo, useCallback, разделение контекстов.

Итоги

  • React DevTools — основной инструмент
  • Profiler покажет лишние рендеры
  • Бесконечный цикл — обычно зависимости
  • Мутация — причина отсутствия обновлений
  • Логирование редьюсера помогает
  • Zustand devtools — для Zustand