Как искать утечку памяти в React?
Рендеринг и производительность
Утечки в React-приложении почти всегда означают, что что-то удерживает ссылки на размонтированные компоненты: подписки, таймеры, замыкания, кеши.
Типичные источники
- Слушатели событий без removeEventListener в cleanup.
- setInterval/setTimeout без clear.
- Открытые WebSocket/EventSource, IntersectionObserver/ResizeObserver без disconnect.
- Подписки на сторы и внешние библиотеки без unsubscribe.
- Растущие кеши/массивы в модульной области или ref (буферы событий, история).
- Замыкания в долгоживущих объектах, держащие props/state старого рендера.
- Детачед DOM-узлы, сохранённые в ref после размонтирования.
Как искать
Chrome DevTools → Performance monitor: смотрим JS heap size и DOM nodes при повторении сценария (открыть/закрыть модалку 20 раз). Растут и не падают после GC — утечка.
Memory → Heap snapshot: сделать снимок, повторить сценарий, снять второй, сравнить (Comparison): что за объекты добавились. Ищем Detached DOM tree и удерживаемые компоненты; в «Retainers» видно, кто держит ссылку (обычно ваш обработчик или таймер).
Allocation instrumentation on timeline — где именно аллоцируется растущая память.
Performance-запись с длинным сценарием: пилообразный график, у которого нижняя граница ползёт вверх, — признак утечки.
Профилактика в коде
- Каждому эффекту с подпиской/таймером — cleanup; проверяет StrictMode (двойное монтирование в dev).
- Отмена запросов через AbortController; флаг «не применять ответ» после размонтирования.
- ESLint react-hooks/exhaustive-deps — уменьшает число «висячих» замыканий.
- Не хранить неограниченно растущие структуры в модульных переменных.
Коротко для собеседования
Искать через DevTools: Performance monitor (heap и DOM nodes не падают после повторов сценария) и сравнение heap-снимков — Detached DOM trees и retainers покажут, кто держит ссылку. Причины: подписки/таймеры/обсерверы без cleanup, незакрытые сокеты, растущие кеши и замыкания. Профилактика — cleanup в каждом эффекте, AbortController, StrictMode.