跪拜 Guibai
← All articles
JavaScript

React Router v7 From Scratch: Lazy Loading, Auth Guards, and Nested Routes

By mONESY ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

React Router v7 solidifies the component-based routing model that most React codebases rely on. Getting the auth guard pattern right—passing state through Navigate and using replace to keep the login page out of history—prevents the redirect bugs that plague nearly every app with protected routes.

Summary

BrowserRouter replaces the hash fragment with clean History API URLs, while Routes and Route turn routing into a declarative component tree. Dynamic segments like `/user/:id` feed parameters into `useParams`, and nested routes render child content through an `<Outlet />` placeholder so parent layouts stay persistent across navigation.

Code-splitting arrives through `React.lazy` and `<Suspense>`, which download page chunks only on first visit. A ProtectRoute wrapper checks `localStorage` and redirects unauthenticated visitors to `/login`, carrying the original path in `location.state` so `useNavigate` can return them exactly where they were headed after a successful login. A wildcard `path="*"` route at the end catches everything else and auto-redirects to the home page after three seconds.

The full architecture strings these together: a global Navigation bar sits outside `<Routes>`, while lazy-loaded pages, nested product routes, a redirect for legacy paths, and the auth guard all coexist inside the same `<Suspense>` boundary.

Takeaways
BrowserRouter produces clean URLs and needs server-side fallback to index.html; HashRouter requires no server config but adds a `#` and hurts SEO.
`React.lazy` with `<Suspense>` splits each page into its own JS chunk, loaded only when the route is first visited.
Dynamic route parameters defined with `:id` are extracted via the `useParams` hook.
Nested routes use a parent `<Route>` with child `<Route>` entries; the parent renders an `<Outlet />` where matched children appear.
A ProtectRoute component wraps protected pages as `children`, checks `localStorage`, and redirects to `/login` with the original path stored in `location.state`.
`<Navigate replace to="..." />` swaps the current history entry so users cannot back-navigate into the login page after authenticating.
A wildcard `path="*"` route placed last catches all unmatched URLs and can auto-redirect after a timeout.
Conclusions

Storing auth state in localStorage and checking it synchronously inside a route guard is simple but leaves the door open to spoofing; a real app would verify a token or session server-side.

The `children` pattern used in ProtectRoute is the same composition mechanism that powers Modal and layout components, making it a transferable React skill rather than a routing-specific trick.

Passing `state` through `<Navigate>` and reading it back with `useLocation` creates a fragile coupling: if a user navigates directly to `/login` without state, the fallback to `"/"` is a silent default that could mask broken deep-link flows.

Concepts & terms
Outlet
A React Router component that acts as a placeholder within a parent route's element. When a child route matches, its element is rendered at the Outlet's position, enabling persistent parent layouts across nested navigation.
Navigate (component)
A declarative redirect component in React Router. When rendered, it immediately changes the current URL. The `replace` prop controls whether the new URL replaces the current history entry, preventing back-navigation to the redirecting page.
React.lazy + Suspense
React.lazy enables dynamic imports so a component's code is fetched only when first rendered. Suspense provides a fallback UI (like a loading spinner) while the lazy component's bundle downloads.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗