Site logo
Tác giả
  • avatar Nguyễn Đức Xinh
    Name
    Nguyễn Đức Xinh
    Twitter
Ngày xuất bản
Ngày xuất bản

React Router: Hướng Dẫn Hoàn Chỉnh về Routing trong React Apps

Tổng Quan về React Router

React Router là library routing declarative cho React applications. Với hơn 56.1k stars trên GitHub và được sử dụng bởi 11 triệu repositories, React Router là standard để xây dựng single-page applications (SPA) với multiple views.

React Router v7 là phiên bản mới nhất, đóng vai trò như multi-strategy router bridging từ React 18 đến React 19, có thể sử dụng như một React framework hoặc minimal library.

Lợi Ích Chính

  • Routing Khai Báo: Định nghĩa các route sử dụng JSX components
  • Routing Lồng Nhau: Hỗ trợ cấu trúc bố cục phức tạp
  • Chia Tách Code: Tải chậm theo từng route để tối ưu hiệu suất
  • Tải Dữ Liệu: Khả năng tích hợp sẵn để lấy dữ liệu
  • Quản Lý Lịch Sử: Tích hợp với lịch sử trình duyệt
  • Hỗ Trợ TypeScript: Tích hợp TypeScript hoàn chỉnh

Các Chế Độ React Router

React Router v7 cung cấp 3 chế độ chính để sử dụng:

  1. Chế Độ Khai Báo - Routing khai báo truyền thống
  2. Chế Độ Dữ Liệu - Routing dựa trên dữ liệu với các bộ tải
  3. Chế Độ Framework - React framework đầy đủ tính năng

Yêu Cầu Hệ Thống

  • React: 18+ (khuyến nghị 18.2+)
  • Node.js: 20.19+ hoặc phiên bản mới hơn
  • TypeScript: 5.0+ (tùy chọn nhưng được khuyến nghị)
  • Trình Quản Lý Package: npm, yarn, pnpm, hoặc bun

Cài Đặt React Router

# Cách 1: Sử dụng react-router trực tiếp (được khuyến nghị)
pnpm install react-router

# Cách 2: Sử dụng react-router-dom (wrapper cho compatibility)
pnpm install react-router-dom@7
# TypeScript types đã được bao gồm trong cả hai packages

Lưu Ý Quan Trọng: react-router-dom vs react-router

Sự Khác Biệt Chính

  • react-router: Package chính chứa tất cả logic routing core
  • react-router-dom: Trong v7 chỉ là "wrapper" re-export từ react-router
  • Khuyến nghị: Sử dụng trực tiếp react-router cho projects mới

Tại Sao Có Sự Thay Đổi Này?

  1. Đơn giản hóa: Giảm confusion giữa hai packages
  2. Tương lai: react-router-dom sẽ được deprecated trong các phiên bản tiếp theo
  3. Hiệu suất: Ít dependency hơn, bundle size nhỏ hơn

Migration Path

// # Cũ (v6)
import { BrowserRouter } from "react-router-dom"

// # Mới (v7) - Khuyến nghị
import { BrowserRouter } from "react-router"

Thiết Lập Routing Cơ Bản

Chế Độ Khai Báo - Thiết Lập Đơn Giản

Tạo basic routing structure trong src/main.tsx:

// src/main.tsx
import { StrictMode } from "react";
import React from 'react'
import { createRoot } from "react-dom/client";
import { BrowserRouter, Routes, Route } from 'react-router'
import App from './App'
import Home from './pages/Home'
import About from './pages/About'
import NotFound from './pages/NotFound'
import './index.css'

createRoot.createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<App />}>
          <Route index element={<Home />} />
          <Route path="about" element={<About />} />
          <Route path="*" element={<NotFound />} />
        </Route>
      </Routes>
    </BrowserRouter>
  </StrictMode>,
)

Chế độ Data

Bằng cách tách cấu hình định tuyến ra khỏi quá trình render của React, Data Mode bổ sung thêm chức năng tải dữ liệu, hành động, trạng thái chờ xử lý và nhiều hơn nữa với các API như loader, action và useFetcher.

import {
  createBrowserRouter,
  RouterProvider,
} from "react-router";

let router = createBrowserRouter([
  {
    path: "/",
    Component: Root,
    loader: loadRootData,
  },
  {
    path: "/about",
    Component: About,
    loader: loadAboutData,
    ...
  }
]);

ReactDOM.createRoot(root).render(
  <RouterProvider router={router} />,
);

Component Bố Cục với Outlet

Tạo component bố cục trong src/App.tsx:

// src/App.tsx
import { Outlet, Link, useLocation } from 'react-router'

function App() {

  return (
    <div className="min-h-screen bg-gray-50">
      {/* Navigation Header */}
      <nav className="bg-white shadow-md">
        <div className="max-w-6xl mx-auto px-4">
          <div className="flex justify-between items-center py-4">
            <Link to="/" className="text-2xl font-bold text-blue-600">
              My App
            </Link>
            
            <div className="flex space-x-6">
              <NavLink to="/">Home</NavLink>
              <NavLink to="/about">About</NavLink>
              <NavLink to="/contact">Contact</NavLink>
            </div>
          </div>
        </div>
      </nav>

      {/* Main Content */}
      <main className="max-w-6xl mx-auto px-4 py-8">
        <Outlet />
      </main>

      {/* Footer */}
      <footer className="bg-gray-800 text-white text-center py-6">
        <p>&copy; 2025 My App. All rights reserved.</p>
      </footer>
    </div>
  )
}

// NavLink với styling
function NavLink({ to, children }: { to: string; children: React.ReactNode }) {
  const location = useLocation()
  const isActive = location.pathname === to

  return (
    <Link
      to={to}
      className={`px-3 py-2 rounded-md transition-colors ${
        isActive
          ? 'bg-blue-600 text-white'
          : 'text-gray-700 hover:bg-blue-50 hover:text-blue-600'
      }`}
    >
      {children}
    </Link>
  )
}

export default App

Các Component Page

Tạo các component cho từng Page:

// src/pages/Home.tsx
export default function Home() {
  return (
    <div>
      <h1 className="text-4xl font-bold text-gray-900 mb-6">
        Welcome Home
      </h1>
      <p className="text-xl text-gray-600 mb-8">
        This is the home page of our React Router application.
      </p>
      
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
        <FeatureCard 
          title="Fast Routing"
          description="Lightning-fast client-side routing"
        />
        <FeatureCard 
          title="Code Splitting"
          description="Lazy load components for better performance"
        />
        <FeatureCard 
          title="TypeScript"
          description="Full TypeScript support out of the box"
        />
      </div>
    </div>
  )
}

function FeatureCard({ title, description }: { title: string; description: string }) {
  return (
    <div className="bg-white p-6 rounded-lg shadow-md border border-gray-200">
      <h3 className="text-xl font-semibold text-gray-900 mb-3">{title}</h3>
      <p className="text-gray-600">{description}</p>
    </div>
  )
}
// src/pages/About.tsx
export default function About() {
  return (
    <div>
      <h1 className="text-4xl font-bold text-gray-900 mb-6">About Us</h1>
      <div className="prose max-w-none">
        <p className="text-xl text-gray-600 mb-6">
          Learn more about our mission and values.
        </p>
        <p className="text-gray-700 mb-4">
          We are dedicated to providing excellent React Router examples
          and tutorials for developers around the world.
        </p>
      </div>
    </div>
  )
}
// src/pages/NotFound.tsx
import { Link } from 'react-router'

export default function NotFound() {
  return (
    <div className="text-center">
      <h1 className="text-6xl font-bold text-gray-900 mb-4">404</h1>
      <h2 className="text-2xl text-gray-600 mb-6">Page Not Found</h2>
      <p className="text-gray-500 mb-8">
        The page you're looking for doesn't exist.
      </p>
      <Link 
        to="/" 
        className="bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-lg transition-colors"
      >
        Go Home
      </Link>
    </div>
  )
}

Khái Niệm Routing Nâng Cao

Route Động với Tham Số

// src/main.tsx - Thêm các route động
<Routes>
  <Route path="/" element={<App />}>
    <Route index element={<Home />} />
    <Route path="users" element={<Users />} />
    <Route path="users/:userId" element={<UserProfile />} />
    <Route path="users/:userId/posts/:postId" element={<Post />} />
    <Route path="*" element={<NotFound />} />
  </Route>
</Routes>
// src/pages/UserProfile.tsx
import { useParams, useNavigate } from 'react-router'

export default function UserProfile() {
  const { userId } = useParams<{ userId: string }>()
  const navigate = useNavigate()

  return (
    <div className="max-w-2xl mx-auto">
      ...
      <button 
      onClick={() => navigate('/users')}
      className="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-md transition-colors"
    >
      Quay Lại Danh Sách
    </button>
    </div>
  )
}

Route Được Bảo Vệ với Xác Thực

// src/components/ProtectedRoute.tsx
import { Navigate, useLocation } from 'react-router'
import { useAuth } from '../hooks/useAuth'

interface ProtectedRouteProps {
  children: React.ReactNode
}

export default function ProtectedRoute({ children }: ProtectedRouteProps) {
  const { isAuthenticated, loading } = useAuth()
  const location = useLocation()

  if (loading) {
    return (
      <div className="flex items-center justify-center min-h-screen">
        <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
      </div>
    )
  }

  if (!isAuthenticated) {
    // Chuyển hướng đến trang đăng nhập với return url
    return <Navigate to="/login" state={{ from: location }} replace />
  }

  return <>{children}</>
}

Kết Luận

React Router mang đến giải pháp Routing mạnh mẽ cho các ứng dụng React với:

Tiếp Cận Đa Chiến Lược: Các chế độ Khai Báo, Dữ Liệu và Framework
Hiệu Suất Cải Tiến: Chia tách code tích hợp sẵn và tối ưu hóa
TypeScript Xuất Sắc: An toàn kiểu dữ liệu hoàn chỉnh với các pattern hiện đại
Tải Dữ Liệu: Các bộ tải và hành động nâng cao
Trải Nghiệm Lập Trình: Công cụ và debug xuất sắc
Sẵn Sàng Cho Tương Lai: Hỗ trợ các tính năng concurrent của React 18+

React Router tiếp tục là chuẩn mực cho client-side routing trong hệ sinh thái React! 🚀

Tài Liệu Tham Khảo