npm install react-router-dom
// main.tsx
import { BrowserRouter } from 'react-router-dom';
createRoot(document.getElementById('root')!).render(
<BrowserRouter>
<App />
</BrowserRouter>
);
import { Routes, Route, Link, NavLink } from 'react-router-dom';
function App() {
return (
<div>
{/* Navigation */}
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
{/* NavLink tự động thêm class "active" */}
<NavLink
to="/products"
className={({ isActive }) => isActive ? 'nav-active' : ''}
>
Products
</NavLink>
</nav>
{/* Routes */}
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/products" element={<Products />} />
<Route path="/products/:productId" element={<ProductDetail />} />
<Route path="*" element={<NotFound />} />
</Routes>
</div>
);
}
import { useParams, useSearchParams } from 'react-router-dom';
// URL: /products/42
function ProductDetail() {
const { productId } = useParams<{ productId: string }>();
return <div>Product ID: {productId}</div>;
}
// URL: /products?category=electronics&page=2
function ProductList() {
const [searchParams, setSearchParams] = useSearchParams();
const category = searchParams.get('category') ?? 'all';
const page = Number(searchParams.get('page') ?? '1');
function handleCategoryChange(newCategory: string) {
setSearchParams((prev) => {
prev.set('category', newCategory);
prev.set('page', '1'); // Reset page khi đổi category
return prev;
});
}
return (
<div>
<select
value={category}
onChange={(e) => handleCategoryChange(e.target.value)}
>
<option value="all">All</option>
<option value="electronics">Electronics</option>
<option value="clothing">Clothing</option>
</select>
<p>Category: {category}, Page: {page}</p>
</div>
);
}
// Layout component với Outlet
import { Outlet, Link } from 'react-router-dom';
function DashboardLayout() {
return (
<div className="dashboard">
<aside>
<Link to="/dashboard">Overview</Link>
<Link to="/dashboard/analytics">Analytics</Link>
<Link to="/dashboard/settings">Settings</Link>
</aside>
<main>
<Outlet /> {/* Render child route content */}
</main>
</div>
);
}
// Route config
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardOverview />} /> {/* /dashboard */}
<Route path="analytics" element={<Analytics />} /> {/* /dashboard/analytics */}
<Route path="settings" element={<Settings />} /> {/* /dashboard/settings */}
<Route path="users/:userId" element={<UserDetail />} />{/* /dashboard/users/:userId */}
</Route>
</Routes>
);
}
import { Navigate, Outlet, useLocation } from 'react-router-dom';
import { useAuth } from './hooks/useAuth';
// Protected Route component
function ProtectedRoute({ requiredRole }: { requiredRole?: string }) {
const { isAuthenticated, user } = useAuth();
const location = useLocation();
if (!isAuthenticated) {
// Lưu location để redirect sau khi login
return <Navigate to="/login" state={{ from: location }} replace />;
}
if (requiredRole && !user?.roles.includes(requiredRole)) {
return <Navigate to="/unauthorized" replace />;
}
return <Outlet />;
}
// Sử dụng
function App() {
return (
<Routes>
{/* Public routes */}
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
{/* Protected routes */}
<Route element={<ProtectedRoute />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
</Route>
{/* Admin only */}
<Route element={<ProtectedRoute requiredRole="admin" />}>
<Route path="/admin" element={<AdminPanel />} />
</Route>
</Routes>
);
}
// Login redirect sau khi đăng nhập thành công
function Login() {
const navigate = useNavigate();
const location = useLocation();
const { login } = useAuth();
const from = (location.state as { from?: Location })?.from?.pathname ?? '/dashboard';
async function handleSubmit(credentials: Credentials) {
await login(credentials);
navigate(from, { replace: true }); // Redirect về trang trước
}
return <LoginForm onSubmit={handleSubmit} />;
}
import { useNavigate } from 'react-router-dom';
function ProductForm() {
const navigate = useNavigate();
async function handleSubmit(data: ProductData) {
const product = await createProduct(data);
// Navigate với state
navigate(`/products/${product.id}`, {
replace: false, // Thêm vào history (default)
state: { message: 'Product created successfully!' },
});
}
return (
<form onSubmit={handleSubmit}>
{/* ... */}
<button type="button" onClick={() => navigate(-1)}>
Go Back
</button>
<button type="button" onClick={() => navigate('/products')}>
Cancel
</button>
</form>
);
}
// Đọc state từ navigation
function ProductDetail() {
const location = useLocation();
const message = (location.state as { message?: string })?.message;
return (
<div>
{message && <Alert>{message}</Alert>}
{/* ... */}
</div>
);
}
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
// Lazy load components
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Analytics = lazy(() => import('./pages/Analytics'));
const AdminPanel = lazy(() => import('./pages/AdminPanel'));
function App() {
return (
<Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/analytics" element={<Analytics />} />
<Route path="/admin" element={<AdminPanel />} />
</Routes>
</Suspense>
);
}
| Link / NavLink | useNavigate |
| Dùng khi | Navigation trong JSX | Navigation trong event handlers |
| Accessibility | ✅ Semantic <a> tag | ❌ Cần thêm manually |
| Ví dụ | Menu items, buttons | Form submit, callback |