Загрузка, отсутствие заявки и восстановление после ошибки
Оглавление курса · Скачать все состояния проекта.
Учебная задача: Различить ожидание, неизвестный ресурс и отказ получения данных.
Входное состояние: Есть список, поиск и динамическая карточка. Выходное состояние: Определены интерфейсы ожидания, отсутствия и ошибки с повторным получением. Продолжаем «Смену» — учебную диспетчерскую заявок мастерской. Нужны основы JavaScript, TypeScript, React и HTTP. Используем App Router, Next 16.4.0, React 19.3.0 и Node.js не ниже 20.9.0. Основные версии закреплены; диапазоны остальных пакетов ещё не разрешены установкой и lockfile отсутствует. Полный каталог lesson-06 в архиве соответствует окончанию этой главы. Выбирайте один снимок целиком: перенос отдельных файлов из будущих этапов меняет его зависимости и правила данных.
Исходники подготовлены редакционным чтением. Пакеты, сервер, сборка, миграции, примеры и внешнее размещение не выполнялись. Ответы страницы и изменения данных ниже являются ожидаемыми, example_execution: not_run.
Пустой список, медленный источник и исключение требуют разных объяснений для диспетчера. Пустой поиск означает, что подходящих заявок нет. Загрузка означает, что результат ещё ожидается. Ошибка означает, что получение не завершилось нормальным результатом. Разведём эти случаи специальными файлами маршрута, чтобы человек не видел один и тот же текст при принципиально разных состояниях.
Файл loading.tsx описывает запасной интерфейс для ожидания в сегменте. Next создаёт соответствующую границу Suspense вокруг дочерней части. Компоновка может оставаться доступной, пока страница получает данные. Наш текст «Получаем заявки…» имеет role="status" и обозначает происходящее действие. Он не сообщает, что операция уже закончилась, и не заменяет сообщение об отсутствии совпадений.
Отсутствие не является произвольной ошибкой
Карточка вызывает notFound(), если поиск не дал объект. Специальная страница отсутствия предлагает вернуться к заявкам. Такой ответ связан с неизвестным ресурсом, а не с отказом базы. Если будущий источник данных недоступен, не стоит маскировать это состоянием «заявки нет»: человек сделает неверный вывод, а разработчик потеряет различие причин.
Состояние отсутствия может возникнуть и при запрещённом объекте, если серверная выборка ищет только разрешённые записи. Позже сотрудник не найдёт чужую заявку в своей выборке. В обоих случаях карточка не должна раскрыть содержимое. При этом внутренний анализ доступа и схема диагностики остаются отдельными задачами. Общий интерфейс 404 не доказывает, что каждая причина отсутствия буквально одинакова.
error.tsx является Client Component. Граница ошибок должна предоставить человеку интерактивную возможность повторить получение. Она получает объект ошибки и функцию восстановления, но мы не показываем сырой текст исключения. В серверных ответах Next может скрывать детали и предоставлять digest для сопоставления с диагностикой. Посетителю полезнее короткое назначение и понятное действие, чем внутренние имена таблиц или строка подключения.
Функция retry в выбранной версии
В актуальном договоре Next 16.4 используем retry(). Она повторяет получение серверной части и помогает восстановиться после временного отказа. В старых примерах можно встретить reset(), которое сбрасывает клиентскую границу и само по себе не гарантирует нового серверного получения. Выбранный API важен именно из-за смысла кнопки: обещаем повторную попытку получения, поэтому используем соответствующий механизм текущей версии.
Повторная попытка не делает постоянную ошибку временной. Если отсутствует обязательная настройка базы или схема не подготовлена, новый запрос встретит ту же причину. В интерфейсе остаётся действие повторения, но разработчик должен исправить конфигурацию. Не добавляем бесконечный автоматический цикл: он усложнил бы поведение и мог бы нагружать источник, не помогая пользователю понять состояние.
Граница error.tsx относится к вложенной части сегмента и не ловит ошибку собственной компоновки того же уровня. Для такой ошибки нужна граница выше. Это следствие дерева React и маршрута: оболочка находится вокруг обработанной части, а не внутри себя. Поэтому полезно рисовать вложение файлов, прежде чем переносить все сообщения об ошибках в один произвольный каталог.
Ошибка корневого документа
global-error.tsx обслуживает ошибку корневой компоновки и заменяет документ. В нём явно возвращаем html и body. Обычный error.tsx этого не делает, поскольку работает внутри существующей оболочки. Пропуск элементов документа в глобальном варианте нарушил бы его собственный договор. CSS и провайдеры корня также нельзя считать доступными, если корень заменён аварийным интерфейсом.
В режиме разработки могут появляться диагностические средства Next вместо привычного внешнего вида глобальной ошибки. Поэтому чтение файла не равно подтверждённой визуальной проверке такого режима. В нашем архиве подготовлены оба уровня, но ошибка приложения специально не создавалась и сервер не запускался. Ожидаемый внешний ответ описан по документации и структуре исходников.
Рассмотрите четыре ситуации. Поиск без совпадений возвращает обычную страницу с пустым набором. Неизвестный id вызывает отсутствие карточки. Медленный асинхронный источник оставляет интерфейс ожидания до результата. Отказ источника приводит к границе ошибок. Во всех случаях человек может не видеть заявки, но значение этого состояния различается. Правильная надпись помогает выбрать следующее действие.
Для самостоятельного разбора укажите, какая граница охватывает страницу карточки и какая нужна для ошибки рабочей компоновки. Затем объясните, почему кнопка повторения не должна запускать создание заявки заново. Получение данных и предметная мутация имеют разные последствия. Следующие главы используют эти интерфейсы при подключении реальных источников, сохраняя смысл каждого состояния.
Проверьте смысл границ
Технический текст исключения можно использовать только в предусмотренной диагностике с учётом содержания. В интерфейсе посетителя задача другая: назвать отказ и дать следующий шаг. Для базы это может быть повторение или обращение к обслуживающему сотруднику; для неизвестного адреса переход к списку. Не оставляйте одну кнопку с надписью «исправить всё», которая не соответствует конкретной операции.
Исключения обработчиков событий клиентского компонента также требуют своего управления: граница ошибок не является универсальным catch для любого асинхронного обработчика. Поэтому будущие кнопки отдельно обработают результат серверного действия. Файловая граница обслуживает соответствующую часть построения маршрута, а предметный отказ формы возвращается её собственным состоянием.
Полные файлы этого шага
Следующие файлы показаны полностью. Остальные согласованные компоненты и настройки находятся в том же каталоге архива; сохраняйте их вместе при самостоятельном прохождении.
app/(work)/tickets/loading.tsx
export default function Loading() { return <main><p role="status">Получаем заявки…</p></main>; }
app/(work)/tickets/error.tsx
"use client";
export default function TicketError({ retry }: { error: Error & { digest?: string }; retry: () => void }) {
return <main><h1>Заявки временно недоступны</h1><p>Повторите получение данных.</p><button onClick={() => retry()}>Повторить</button></main>;
}
app/not-found.tsx
import Link from "next/link";
export default function NotFound() { return <main><h1>Страница не найдена</h1><Link href="/tickets">К заявкам</Link></main>; }
app/global-error.tsx
"use client";
export default function GlobalError({ retry }: { error: Error & { digest?: string }; retry: () => void }) {
return <html lang="ru"><body><h1>Не удалось открыть «Смену»</h1><button onClick={() => retry()}>Повторить</button></body></html>;
}
Документация и исходники
Договор этой главы раскрыт в официальной документации. Сопоставьте её API с выбранной версией и с объяснённым источником каждого значения. Архив «Смены» содержит двадцать полных состояний и условия самостоятельной подготовки. Ожидаемый результат оценивается по текущей главе: поздние проверки владельца, база и внешние ключи не являются неявно выполненными предпосылками раннего учебного состояния.