React роутинг: что спрашивают на интервью и как отвечать уверенно
Разбираем React роутинг для собеседований: client-side navigation, nested routes, params и search params, protected routes, data loading, code splitting, production-ошибки и сильные ответы.
- Введение
- Что обычно имеют в виду под React роутингом
- Client-side routing против server-side routing
- Архитектура роутинга: как мыслят зрелые команды
- Какие слои обычно есть
- Поток данных и управления
- Базовый пример: nested routes и layout
- Params, search params и состояние URL
- Path params
- Search params
- Что часто ломают
- Таблица: какие задачи решать каким уровнем роутинга
- Protected routes: где кандидат чаще всего ошибается
- Data loading и экранные границы
- Production pitfalls: что реально ломается
- Ошибка 1. Route guard редиректит раньше, чем завершилась проверка пользователя
- Ошибка 2. Фильтры и пагинация не синхронизированы с URL
- Ошибка 3. Каждый route повторно грузит одни и те же данные
- Разбор производительности
- Практики, которые реально работают
- Держите layout-уровни явными
- Храните sharable screen state в URL
- Ставьте route-level error handling осознанно
- Не делайте роутер единственным местом всей бизнес-логики
- Тестируйте не только happy path
- Частые ошибки
- Путать path params и search params
- Делать редиректы через useEffect, когда можно декларативно
- Хранить URL-состояние и local state без стратегии синхронизации
- Недооценивать 404 и route-level ошибки
- Как отвечать на интервью
- FAQ
- Что важнее на интервью: знать API роутера или понимать архитектуру
- Когда фильтр надо класть в URL
- Достаточно ли protected route на клиенте
- Почему nested routes любят на собеседованиях
- Что чаще всего недооценивают в роутинге
- Итоги
Введение
React роутинг на собеседовании редко ограничивается вопросом «что делает Route». Обычно интервьюер через тему маршрутизации проверяет сразу несколько вещей: понимаете ли вы модель SPA-навигации, умеете ли проектировать layout-уровни, различаете ли состояние экрана и состояние компонента, умеете ли работать с загрузкой данных, ошибками и код-сплиттингом.
Из-за этого многие кандидаты отвечают слишком поверхностно: «использовал React Router, настраивал path и navigate». Этого мало. Сильный ответ строится вокруг архитектуры и trade-off’ов. Для базового контекста по клиентской навигации полезно держать рядом подборку junior-вопросов по React.
Больше вопросов в Telegram
Ежедневные разборы и реальные кейсы с интервью.
Что обычно имеют в виду под React роутингом
В React-проектах под роутингом обычно понимают слой, который:
- сопоставляет URL и экран;
- определяет вложенные layout-уровни;
- управляет навигацией между экранами;
- связывает URL с params, query string и частью состояния приложения;
- координирует загрузку данных, fallback-состояния и обработку ошибок.
На практике это не только библиотека маршрутизации, но и архитектура экранов. Как только в приложении появляются dashboard-layout, protected sections, модульные страницы, breadcrumbs, deep links и lazy-загрузка, роутинг перестает быть «несколькими Route в одном файле».
Client-side routing против server-side routing
Первый базовый вопрос на интервью почти всегда про разницу между этими моделями.
Client-side routing в SPA означает, что после первой загрузки документа переходы между экранами происходят без полного reload страницы. Браузер меняет URL, роутер выбирает экран, React обновляет дерево компонентов.
Server-side routing означает, что на каждый переход сервер формирует новую HTML-страницу или новый серверный ответ для целевого URL.
Для React-собеседования важны не определения, а последствия:
- client-side routing дает быстрые переходы после initial load;
- server-side routing помогает с первой загрузкой, SEO и серверным контролем данных;
- в клиентской навигации надо отдельно продумывать loading, data fetching, 404 и защиту приватных экранов;
- в server-first системах часть задач маршрутизации уходит в платформу, но не исчезает архитектурно.
Хороший кандидат явно проговаривает, что client-side routing не отменяет серверную ответственность за доступ к данным. Скрыть route на клиенте недостаточно, если API все равно отдает защищенную информацию.
Архитектура роутинга: как мыслят зрелые команды
Маршрутизация перестает быть хаотичной, когда команда разделяет несколько уровней ответственности.
Какие слои обычно есть
-
Корневой shell. Отвечает за app-wide providers, тему, глобальные boundary, общую навигацию.
-
Layout routes. Описывают общую рамку для группы экранов: sidebar, header, breadcrumbs, access policy.
-
Screen routes. Это конкретные страницы или крупные экранные сценарии.
-
Widget-level components. Обычные React-компоненты внутри страницы, которые не должны знать о роутере больше, чем нужно.
Поток данных и управления
Нормальный поток выглядит так:
- URL приходит в роутер.
- Роутер выбирает цепочку layout + page компонентов.
- На route-уровне определяется, какие данные и проверки нужны.
- Экран получает params, search params и загрузочные данные.
- Внутренние виджеты уже работают как обычные компоненты.
Узкие места начинаются, когда роутер знает слишком мало или слишком много.
Если логика авторизации, фильтров, табов, прелоадов и аналитики размазана по виджетам, приложение становится хрупким. Если же в конфиг роутера запихнули все подряд, он превращается в монолитную карту, которую страшно менять.
Нормальная деградация обычно выглядит так:
- route-level loading не блокирует весь shell;
- локальная ошибка не валит все приложение;
- 404 и access denied имеют отдельные ветки;
- lazy routes загружаются по мере необходимости.
Для этого полезно понимать, где ставятся error boundaries и как они соотносятся с route-уровнем.
Базовый пример: nested routes и layout
На интервью часто просят показать, как устроить вложенные маршруты без копирования layout-кода по страницам.
import {
createBrowserRouter,
Navigate,
Outlet,
RouterProvider,
} from "react-router-dom";
function AppLayout() {
return (
<div className="app-shell">
<header>Lexicon</header>
<main>
<Outlet />
</main>
</div>
);
}
function DashboardLayout() {
return (
<section className="dashboard">
<aside>Sidebar</aside>
<div className="content">
<Outlet />
</div>
</section>
);
}
const router = createBrowserRouter([
{
path: "/",
element: <AppLayout />,
children: [
{ index: true, element: <HomePage /> },
{
path: "dashboard",
element: <DashboardLayout />,
children: [
{ index: true, element: <Navigate to="analytics" replace /> },
{ path: "analytics", element: <AnalyticsPage /> },
{ path: "settings", element: <SettingsPage /> },
],
},
{ path: "*", element: <NotFoundPage /> },
],
},
]);
export function AppRouter() {
return <RouterProvider router={router} />;
}
Что здесь важно объяснить:
Outletзадает точку вложенного рендера;- layout routes позволяют не дублировать shell;
- index route полезен для дефолтного дочернего экрана;
- wildcard-ветка нужна для 404;
- редирект внутри вложенного layout лучше держать явным, а не размазывать по
useEffect.
Если кандидат умеет объяснить именно это, а не только синтаксис, ответ уже выглядит уровнем выше.
Params, search params и состояние URL
Очень частый блок вопросов: что держать в path params, что в query string, а что в локальном state.
Path params
Их используют, когда значение определяет ресурс или экран:
/users/:id/orders/:orderId/courses/:slug/lessons/:lessonId
Search params
Они подходят для экранного состояния, которое:
- должно переживать refresh;
- должно шариться ссылкой;
- влияет на представление данных, а не на сам ресурс.
Типичные примеры:
?page=3?sort=price_desc?tab=activity?query=react
Что часто ломают
Кандидаты нередко держат фильтры только в local state. В коротком демо это нормально, но в production пользователь теряет состояние после refresh, не может поделиться ссылкой и получает странное поведение back/forward navigation.
Сильный ответ: URL должен хранить экранный контракт. Если без этого параметра нельзя восстановить тот же самый экран, есть шанс, что ему место в URL.
Пример синхронизации search params:
import { useSearchParams } from "react-router-dom";
function UsersPage() {
const [searchParams, setSearchParams] = useSearchParams();
const page = Number(searchParams.get("page") ?? "1");
const query = searchParams.get("query") ?? "";
function updateQuery(nextQuery: string) {
const next = new URLSearchParams(searchParams);
if (nextQuery) {
next.set("query", nextQuery);
next.set("page", "1");
} else {
next.delete("query");
next.delete("page");
}
setSearchParams(next);
}
return (
<section>
<SearchBox defaultValue={query} onSearch={updateQuery} />
<UsersTable page={page} query={query} />
</section>
);
}
Тут важно не только уметь читать params, но и объяснить, почему page сбрасывается при изменении поиска: это уже разговор на языке продуктовой логики, а не API.
Таблица: какие задачи решать каким уровнем роутинга
| Сценарий | Local state | Search params | Path params | Route-level layout |
|---|---|---|---|---|
| Открыт ли локальный dropdown | Да | Нет | Нет | Нет |
| Активная страница пагинации | Иногда | Да | Нет | Нет |
Текущий пользователь /users/:id | Нет | Нет | Да | Нет |
| Вкладка внутри экранного сценария | Иногда | Часто да | Редко | Иногда |
| Общая рамка dashboard | Нет | Нет | Нет | Да |
| Фильтры, которыми делятся ссылкой | Плохо | Да | Нет | Нет |
| Защита приватного раздела | Нет | Нет | Нет | Да |
Практический смысл таблицы: роутинг это не только path, а способ правильно разложить состояние и ответственность между URL, layout и компонентами.
Protected routes: где кандидат чаще всего ошибается
На словах почти все говорят: «если пользователь не авторизован, делаем redirect на login». Этого мало.
Protected route в production должен учитывать:
- состояние
loading, пока мы не знаем текущего пользователя; - различие между
unauthenticatedиforbidden; - возврат пользователя на исходный URL после логина;
- ситуацию, когда токен протух в процессе навигации;
- то, что клиентский guard сам по себе не защищает серверные данные.
На практике auth-состояние для таких сценариев часто поднимают через контекст или отдельный session-layer. Поэтому полезно понимать не только роутер, но и когда действительно нужен React Context API.
Базовый паттерн:
import { Navigate, Outlet, useLocation } from "react-router-dom";
function ProtectedRoute() {
const location = useLocation();
const { user, isLoading } = useAuth();
if (isLoading) {
return <RouteSkeleton />;
}
if (!user) {
return (
<Navigate
to="/login"
replace
state={{ from: location.pathname + location.search }}
/>
);
}
return <Outlet />;
}
Хороший комментарий к этому примеру:
- нельзя редиректить до завершения auth-check, иначе будет route flicker;
state.fromнужен для возврата после логина;replaceпомогает не засорять history;- серверная авторизация все равно обязательна.
Если кандидат проговаривает хотя бы половину этих пунктов, это обычно заметно отличает его от ответа уровня «просто проверяю токен в localStorage».
Data loading и экранные границы
Когда разговор заходит глубже, интервьюер часто спрашивает не про сам роутер, а про то, где грузить данные: в компоненте страницы, в route-слое или в кастомных хуках.
Здесь нет одного универсального ответа, но зрелая аргументация выглядит так:
- route-level loading хорош для данных, без которых экран нельзя собрать;
- widget-level loading хорош для вторичных блоков, которые не должны блокировать весь route;
- layout-level loading оправдан, когда данные общие для нескольких дочерних экранов;
- критично не дублировать fetch одних и тех же данных на каждом переходе между соседними route.
Это напрямую связано с Suspense, error boundary и стоимостью лишних ререндеров при навигации.
Production pitfalls: что реально ломается
Ошибка 1. Route guard редиректит раньше, чем завершилась проверка пользователя
Симптомы:
- при refresh приватного экрана пользователя бросает на login и сразу возвращает обратно;
- в логах фронтенда видно двойную навигацию;
- аналитика считает лишние page views.
Последствия:
- route flicker;
- нестабильный UX;
- сложные баги с сохранением intended URL.
Как обнаружить заранее:
- смотреть цепочки navigation в devtools;
- проверять поведение при медленном ответе
/me; - тестировать refresh на приватной странице.
Ошибка 2. Фильтры и пагинация не синхронизированы с URL
Симптомы:
- пользователь делится ссылкой, а коллега видит другой экран;
- после refresh все фильтры сбрасываются;
- кнопки back/forward ведут себя нелогично.
Последствия:
- ломается deep linking;
- растет число повторных действий пользователя;
- усложняется поддержка аналитики и QA.
Как исправить:
- выносить экранное состояние в search params;
- хранить в URL только то, что реально определяет экран;
- не смешивать временный UI-state и sharable state.
Ошибка 3. Каждый route повторно грузит одни и те же данные
Симптомы:
- при переходе между соседними вкладками dashboard заново дергается один и тот же профиль;
- растут latency и network cost;
- пользователь видит постоянные skeleton на каждом клике.
Последствия:
- лишняя нагрузка на API;
- замедление навигации;
- деградация perceived performance.
Как предотвратить:
- поднимать общие данные на layout-level;
- использовать кэширование и нормальную стратегию invalidation;
- разделять критические и вторичные запросы.
Разбор производительности
Сам по себе роутинг редко bottleneck. Обычно узкие места появляются рядом:
- слишком тяжелые route-компоненты;
- повторный data fetching на каждой навигации;
- неудачный code splitting;
- глобальные provider'ы, которые ререндерят весь shell;
- избыточная синхронизация state и URL.
Когда оптимизация оправдана:
- route chunk весит слишком много и мешает первой интерактивности;
- переходы между крупными экранами сопровождаются заметным layout shift;
- на каждый route-change летят дублирующие запросы;
- shell ререндерится целиком из-за изменения локального состояния страницы.
Когда optimization premature:
- у приложения 5 простых страниц;
- навигация и так мгновенная;
- вы еще не измеряли ни bundle, ни network, ни route transitions.
Полезный инженерный ответ здесь звучит так: я бы профилировал переходы, смотрел размер чанков, количество повторных запросов, время route-to-interactive и только потом решал, нужен ли aggressive code splitting или переразбиение layout-уровней. По смежной теме помогает помнить разбор React rendering.
Если разговор уходит в оптимизацию глубже, логично связать routing с типичными вопросами по React performance, потому что route transition почти всегда упирается не в сам роутер, а в лишние ререндеры, тяжелые провайдеры и дублирующий fetch.
Прокачай React за 7 дней
20 вопросов и разборов по React Hooks.
Практики, которые реально работают
Держите layout-уровни явными
Root layout, auth layout, dashboard layout и public layout лучше оформлять отдельно. Это уменьшает дублирование и упрощает доступ, breadcrumbs и общие данные.
Храните sharable screen state в URL
Поиск, page, sort, tab и фильтры, влияющие на представление списка, обычно должны жить в search params.
Ставьте route-level error handling осознанно
Если ошибка одного виджета не должна валить всю страницу, не поднимайте boundary слишком высоко. Если же без данных route экран бессмысленен, boundary на уровне route оправдан.
Не делайте роутер единственным местом всей бизнес-логики
Роутинг должен координировать экран, а не хранить всю логику продукта. Как только конфиг роутера становится местом, где живет половина приложения, сопровождение дорожает.
Тестируйте не только happy path
Нужны как минимум такие сценарии:
- переход на неизвестный URL;
- refresh приватного route;
- возврат после логина на исходный экран;
- навигация back/forward с search params;
- медленный route data fetch;
- отказ одного экрана без падения всего shell.
Частые ошибки
Путать path params и search params
Если page=3 или sort=asc запихивают в path без причины, URL становится хуже масштабируемым. Если userId уезжает в query string, маршрут теряет четкую модель ресурса.
Делать редиректы через useEffect, когда можно декларативно
Декларативный redirect в route-дереве или в guard-компоненте обычно проще, чем императивная навигация после mount.
Хранить URL-состояние и local state без стратегии синхронизации
После этого одни и те же фильтры живут в двух местах, а источник истины непонятен.
Недооценивать 404 и route-level ошибки
На маленьком pet-проекте это не видно, но в production отсутствие отдельного 404 и fallback-стратегии делает навигацию хрупкой и плохо диагностируемой.
Как отвечать на интервью
Хороший каркас ответа на вопрос «что вы знаете про React роутинг?» обычно такой:
- Роутинг в React это не просто
Route, а слой сопоставления URL, layout-уровней, навигации и части экранного состояния. - В SPA используется client-side navigation без полной перезагрузки документа, но это не отменяет серверную ответственность за доступ к данным.
- Важные темы на практике: nested routes, layout routes, params и search params, protected routes, 404, loading/error handling и code splitting.
- URL должен хранить состояние, которое определяет экран и должно переживать refresh или шариться ссылкой.
- Основные ошибки в production: ранние редиректы, дублирование fetch, потеря state при навигации и отсутствие стратегии для loading/error.
Если спрашивают глубже, можно дать быстрые формулировки:
- nested routes нужны для layout composition и уменьшения дублирования;
- protected route это не только redirect, но и корректная обработка auth-loading и forbidden state;
- search params хороши для фильтров, сортировки, табов и пагинации;
- route-level data loading уместен, когда без данных нельзя собрать экран;
- lazy routes полезны, когда вы реально боретесь за initial bundle и route transition cost.
Такой ответ показывает, что вы понимаете маршрутизацию как часть архитектуры приложения, а не как библиотечный API наизусть.
Подготовься к React-интервью на реальных сценариях маршрутизации
Разберем routing, hooks, ререндеры, ошибки и архитектурные trade-off'ы в формате mock-интервью с разбором сильных и слабых ответов.
FAQ
Что важнее на интервью: знать API роутера или понимать архитектуру
Архитектура. Синтаксис Route, Link и navigate можно быстро вспомнить, а вот объяснить, где хранить screen state и как строить protected layout, без понимания модели не получится.
Когда фильтр надо класть в URL
Когда он определяет экран, должен переживать refresh, участвует в deep link и нужен для back/forward navigation.
Достаточно ли protected route на клиенте
Нет. Клиентский guard управляет UX и навигацией, но не заменяет серверную проверку доступа к данным.
Почему nested routes любят на собеседованиях
Потому что через них быстро видно, умеет ли кандидат мыслить layout-уровнями и понимает ли, как не дублировать shell и общую логику по страницам.
Что чаще всего недооценивают в роутинге
404, loading/error states, возврат после логина, синхронизацию search params и влияние роутинга на data fetching и bundle size.
Итоги
React роутинг на интервью это вопрос не про библиотеку, а про экранную архитектуру. Интервьюер хочет понять, умеете ли вы разложить ответственность между URL, route layout, page-компонентом и локальным UI-состоянием.
Если коротко, зрелое понимание темы выглядит так: URL описывает экранный контракт, nested routes собирают layout без дублирования, protected routes управляют UX, но не подменяют серверную безопасность, а loading/error/code splitting проектируются на route-уровне осознанно. Именно такой ответ обычно звучит как опыт работы с production-приложением, а не как пересказ документации.
Больше вопросов в Telegram
Ежедневные разборы и реальные кейсы с интервью.
Автор
Lexicon Team
Читайте также
frontend
System design для frontend разработчика: как мыслить системно, а не только компонентами
Практический разбор system design для frontend разработчика: границы ответственности, данные, производительность, ошибки и сильный ответ на интервью.
frontend
React patterns, которые спрашивают на senior интервью: не определения, а архитектурные компромиссы
Разбираем React patterns для senior интервью: HOC, Render Props, Compound Components, controlled/uncontrolled, headless API и критерии выбора.
frontend
Feature-Sliced Design для React проектов: когда FSD помогает, а когда усложняет код
Практический разбор Feature-Sliced Design для React проектов: слои, public API, правила зависимостей, типичные ошибки, производительность и ответы для интервью.