Use cases/Router spinner — глобальная загрузка навигации
Use cases · 20 反
Router spinner — глобальная загрузка навигации
Паттерн
Lazy-маршруты, resolvers и guards могут грузиться секундами. Пользователю надо показать, что переход обрабатывается — глобальный spinner поверх app shell.
Какую проблему решаем
Состояние «идёт навигация» нельзя описать одним событием — Router генерирует целое семейство: NavigationStart, NavigationEnd, NavigationCancel, NavigationError. Если просто ставить флаг loading=true на Start и забыть про Cancel/Error — spinner повиснет навсегда после отмены навигации guard-ом.
Операторы и зачем они нужны
filter — оставляем только четыре нужных типа событий (Start + три терминальных), игнорируем шум типа RoutesRecognized.
map — превращаем тип события в boolean: NavigationStart → true, всё остальное → false.
startWith(false) — изначально приложение не грузится.
distinctUntilChanged — два Start подряд (при redirect) не дёргают spinner лишний раз.
Подводные камни
Забыть про Cancel/Error — самая частая ошибка. Spinner зависнет true навсегда после отмены guard-ом.
scan со счётчиком loaders нужен только для параллельных загрузок. Router navigation последовательна — счётчик избыточен.
Spinner в каждом page-компоненте — плохая идея. Lazy-загрузка происходит ДО создания компонента, spinner не успеет показаться. Только в app shell.
Что в итоге получаем
Один глобальный поток покрывает весь lifecycle навигации — start, успех, отмену и ошибку — без ручных флагов в компонентах.