Как отлаживать проблему падения фронтенда с ошибкой после нескольких кликов
Что проверяют
Есть ли у вас порядок действий: воспроизвести, найти первую ошибку, понять механизм и закрепить исправление тестом. «После нескольких кликов» это подсказка: что-то накапливается, состояние, подписки, память или конкурирующие запросы. Следом спрашивают, как вы найдёте такую ошибку на проде, где DevTools у пользователя не откроешь.
Как отвечать
- уточнить симптом: белый экран обычно значит необработанное исключение при рендере, React без Error Boundary размонтирует всё дерево. Зависшая вкладка указывает на бесконечный цикл, страница «Опаньки» в Chrome на нехватку памяти;
- воспроизвести: зафиксировать шаги и включить троттлинг сети в DevTools. Гонки часто проявляются только на медленной сети;
- консоль и Sources: первая ошибка по времени, стек с source maps, режим Pause on exceptions, чтобы отладчик остановился в момент падения;
- накопление: в React ищите
useEffectбез функции очистки, из-за чего множатся подписки иaddEventListener, и замыкания со старым состоянием. СообщениеMaximum update depth exceededозначает цикл обновлений; - гонка запросов: быстрые клики шлют несколько запросов, старый ответ приходит последним и перетирает новый. Лечится
AbortControllerили отменой запросов в TanStack Query; - память: во вкладке Memory снимите heap snapshot до и после десяти кликов и сравните в режиме Comparison, отдельно проверьте Detached DOM nodes;
- прод: Sentry или аналог с breadcrumbs показывает клики и запросы перед ошибкой, session replay показывает сам сценарий.
После исправления добавьте тест в Playwright, который повторяет те же клики, и Error Boundary, чтобы падение одного блока не роняло страницу целиком.
Где спотыкаются
Оборачивают подозрительный код в try/catch и считают проблему решённой. Ошибка просто скрыта, а исключения из асинхронных колбэков такой блок без await вообще не ловит.
Отлаживают минифицированный бандл без source maps и гадают по стеку вида a.b is not a function.
Надеются на Error Boundary как на ловушку для всего. Ошибки в обработчиках событий и асинхронном коде он не перехватывает, их нужно обрабатывать отдельно.