React Router 实战案例
实际项目中的 React Router 使用场景和解决方案
目录
认证流程路由
完整的认证路由配置
import { Routes, Route, Navigate } from 'react-router-dom'
import { useAuth } from './hooks/useAuth'
// 公共路由
const PublicRoute = ({ children }) => {
const { isAuthenticated } = useAuth()
if (isAuthenticated) {
return <Navigate to="/dashboard" replace />
}
return children
}
// 保护路由
const ProtectedRoute = ({ children }) => {
const { isAuthenticated, isLoading } = useAuth()
if (isLoading) {
return <LoadingSpinner />
}
if (!isAuthenticated) {
return <Navigate to="/login" replace />
}
return children
}
// 路由配置
function App() {
return (
<Routes>
{/* 公共路由 */}
<Route
path="/login"
element={
<PublicRoute>
<Login />
</PublicRoute>
}
/>
<Route
path="/register"
element={
<PublicRoute>
<Register />
</PublicRoute>
}
/>
{/* 保护路由 */}
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
{/* 默认重定向 */}
<Route path="/" element={<Navigate to="/dashboard" replace />} />
</Routes>
)
}多级导航菜单
动态生成导航菜单
import { useRoutes, useLocation, Link } from 'react-router-dom'
const routes = [
{
path: '/',
element: <Layout />,
children: [
{ path: 'dashboard', element: <Dashboard />, title: '仪表盘' },
{
path: 'users',
element: <Users />,
title: '用户管理',
children: [
{ path: 'list', element: <UserList />, title: '用户列表' },
{ path: 'roles', element: <UserRoles />, title: '角色管理' },
]
},
],
},
]
function Navigation() {
const location = useLocation()
const currentPath = location.pathname
const renderMenu = (items) => {
return items.map(item => (
<div key={item.path}>
<Link
to={item.path}
className={currentPath === item.path ? 'active' : ''}
>
{item.title}
</Link>
{item.children && renderMenu(item.children)}
</div>
))
}
return <nav>{renderMenu(routes[0].children)}</nav>
}面包屑导航
动态面包屑组件
import { useLocation, Link, matchRoutes } from 'react-router-dom'
const routes = [
{ path: '/', title: '首页' },
{ path: '/dashboard', title: '仪表盘' },
{ path: '/users', title: '用户管理' },
{ path: '/users/:id', title: '用户详情' },
]
function Breadcrumb() {
const location = useLocation()
const matches = matchRoutes(routes, location)
return (
<nav>
{matches?.map((match, index) => {
const isLast = index === matches.length - 1
const route = routes.find(r => r.path === match.route.path)
return (
<span key={match.pathname}>
{isLast ? (
<span>{route?.title}</span>
) : (
<Link to={match.pathname}>{route?.title}</Link>
)}
{!isLast && <span> / </span>}
</span>
)
})}
</nav>
)
}路由权限管理
基于角色的路由控制
import { Navigate, useRoutes } from 'react-router-dom'
import { useAuth } from './hooks/useAuth'
const RoleBasedRoute = ({ children, allowedRoles }) => {
const { user } = useAuth()
if (!user) {
return <Navigate to="/login" replace />
}
if (!allowedRoles.includes(user.role)) {
return <Navigate to="/unauthorized" replace />
}
return children
}
const routes = [
{
path: '/',
element: <Layout />,
children: [
{
path: 'admin',
element: (
<RoleBasedRoute allowedRoles={['admin', 'superadmin']}>
<AdminPanel />
</RoleBasedRoute>
),
},
{
path: 'editor',
element: (
<RoleBasedRoute allowedRoles={['admin', 'editor']}>
<EditorPanel />
</RoleBasedRoute>
),
},
],
},
]
function App() {
const element = useRoutes(routes)
return element
}路由动画过渡
使用 Framer Motion
import { AnimatePresence, motion } from 'framer-motion'
import { useLocation, Routes, Route } from 'react-router-dom'
const pageVariants = {
initial: { opacity: 0, x: -20 },
animate: { opacity: 1, x: 0 },
exit: { opacity: 0, x: 20 },
}
function AnimatedRoutes() {
const location = useLocation()
return (
<AnimatePresence mode="wait">
<Routes location={location} key={location.pathname}>
<Route
path="/"
element={
<motion.div
variants={pageVariants}
initial="initial"
animate="animate"
exit="exit"
>
<Home />
</motion.div>
}
/>
<Route
path="/about"
element={
<motion.div
variants={pageVariants}
initial="initial"
animate="animate"
exit="exit"
>
<About />
</motion.div>
}
/>
</Routes>
</AnimatePresence>
)
}路由状态持久化
保存和恢复路由状态
import { useEffect } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
function useRoutePersistence() {
const location = useLocation()
const navigate = useNavigate()
// 保存路由状态
useEffect(() => {
const routeData = {
pathname: location.pathname,
search: location.search,
state: location.state,
timestamp: Date.now(),
}
localStorage.setItem('lastRoute', JSON.stringify(routeData))
}, [location])
// 恢复路由状态
useEffect(() => {
const savedRoute = localStorage.getItem('lastRoute')
if (savedRoute) {
const routeData = JSON.parse(savedRoute)
const isExpired = Date.now() - routeData.timestamp > 24 * 60 * 60 * 1000 // 24小时
if (!isExpired) {
navigate(routeData.pathname + routeData.search, {
state: routeData.state,
replace: true,
})
}
}
}, [])
}
// 使用
function App() {
useRoutePersistence()
return <Routes>{/* 路由配置 */}</Routes>
}最后更新:2025-12-12