0
0
Lập trình
Admin Team
Admin Teamtechmely

Xây dựng Sidebar Quản Trị Đ collapsible với React Router và useLocation

Đăng vào 4 tháng trước

• 8 phút đọc

Xây dựng Sidebar Quản Trị Đ collapsible với React Router và useLocation

Giới thiệu

Trong thời đại công nghệ hiện đại, các bảng điều khiển (dashboard) yêu cầu phải có điều hướng (navigation) linh hoạt, phản ứng nhanh và nhạy bén với ngữ cảnh. Trong các ứng dụng sử dụng React Router, hook useLocation là công cụ hữu ích để đọc URL hiện tại và thực hiện các quyết định giao diện người dùng — ví dụ như làm nổi bật các mục menu đang hoạt động trong sidebar.

Trong bài viết này, chúng ta sẽ xây dựng một Sidebar Quản Trị Đ collapsible với những tính năng sau:

✅ Sử dụng useLocation để phát hiện và làm nổi bật các route đang hoạt động
✅ Sử dụng biểu tượng Lucide React để tạo giao diện hiện đại
✅ Tính năng chuyển đổi trạng thái collapsible (ChevronLeft / ChevronRight)
✅ Thiết kế đáp ứng và dễ tiếp cận


Tại sao sử dụng useLocation?

Hook useLocation của React Router cung cấp một đối tượng với URL hiện tại, bao gồm pathname, search, và hash. Trong các bảng điều khiển, điều này thường được sử dụng cho:

  • Trạng thái hoạt động: Làm nổi bật mục menu hiện tại.
  • Điều kiện ngữ cảnh: Kiểm tra xem route có khớp với một sub-path hay không (/admin/products/123).
  • Ghi nhật ký / Phân tích: Ghi lại các thay đổi path hoặc gửi các sự kiện phân tích.

Cài đặt Sidebar

javascript Copy
import { Link, useLocation } from 'react-router-dom';
import {
  Home,
  Users,
  BarChart3,
  Settings,
  FileText,
  ShoppingCart,
  Bell,
  HelpCircle,
  ChevronLeft,
  ChevronRight,
} from 'lucide-react';
import { CustomLogo } from '@/components/custom/CustomLogo';

interface SidebarProps {
  isCollapsed: boolean;
  onToggle: () => void;
}

export const AdminSidebar: React.FC<SidebarProps> = ({
  isCollapsed,
  onToggle,
}) => {
  const { pathname } = useLocation();

  const menuItems = [
    { icon: Home, label: 'Dashboard', to: '/admin' },
    { icon: BarChart3, label: 'Products', to: '/admin/products' },
    { icon: Users, label: 'Users' },
    { icon: ShoppingCart, label: 'Orders' },
    { icon: FileText, label: 'Reports' },
    { icon: Bell, label: 'Notifications' },
    { icon: Settings, label: 'Settings' },
    { icon: HelpCircle, label: 'Aid' },
  ];

  const isActiveRoute = (to: string) => {
    if (pathname.includes('/admin/products/') && to === '/admin/products') {
      return true;
    }
    return pathname === to;
  };

  return (
    <div
      className={`bg-white border-r border-gray-200 transition-all duration-300 ease-in-out ${
        isCollapsed ? 'w-18' : 'w-64'
      } flex flex-col`}
    >
      {/* Header */}
      <div className="p-4 border-b border-gray-200 flex items-center justify-between h-18">
        {!isCollapsed && <CustomLogo />}
        <button
          onClick={onToggle}
          className="p-2 rounded-lg hover:bg-gray-100 transition-colors"
        >
          {isCollapsed ? <ChevronRight size={20} /> : <ChevronLeft size={20} />}
        </button>
      </div>

      {/* Navigation */}
      <nav className="flex-1 p-4">
        <ul className="space-y-2">
          {menuItems.map((item, index) => {
            const Icon = item.icon;
            return (
              <li key={index}>
                <Link
                  to={item.to || '/admin'}
                  className={`flex items-center space-x-3 px-3 py-2 rounded-lg transition-all duration-200 group ${
                    isActiveRoute(item.to || '/xxxx')
                      ? 'bg-blue-50 text-blue-600 border-r-2 border-blue-600'
                      : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'
                  }`}
                >
                  <Icon size={20} className="flex-shrink-0" />
                  {!isCollapsed && (
                    <span className="font-medium">{item.label}</span>
                  )}
                </Link>
              </li>
            );
          })}
        </ul>
      </nav>

      {/* User Profile */}
      {!isCollapsed && (
        <div className="p-4 border-t border-gray-200">
          <div className="flex items-center space-x-3 p-3 rounded-lg hover:bg-gray-50 transition-colors cursor-pointer">
            <div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center text-white font-semibold">
              JD
            </div>
            <div className="flex-1 min-w-0">
              <p className="text-sm font-medium text-gray-900 truncate">
                John Doe
              </p>
              <p className="text-xs text-gray-500 truncate">john@company.com</p>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

Các Khái Niệm Chính Trong Ví Dụ Này

Tính năng Mục đích
useLocation Đọc pathname hiện tại để xác định trạng thái hoạt động
isActiveRoute() Logic tùy chỉnh cho so sánh chính xác và lồng ghép
Biểu tượng Lucide Hệ thống biểu tượng nhẹ, đồng nhất (lucide-react)
Sidebar collapsible Mô hình UX để tiết kiệm không gian màn hình
Các lớp TailwindCSS Quản lý khoảng cách, biên, trạng thái hover và chuyển tiếp

Các Mô Hình Nâng Cao

  • Trạng thái collapsible bền vững: Lưu isCollapsed trong localStorage hoặc ngữ cảnh để ghi nhớ sở thích của người dùng.
  • Menu theo vai trò: Lọc menuItems dựa trên vai trò của người dùng.
  • Breadcrumbs: Tạo breadcrumbs từ pathname.split('/').
  • Phân tích: Gửi sự kiện mỗi khi pathname thay đổi với một useEffect trên location.

Tóm tắt

Với chỉ một vài hooks và thiết kế hợp lý, bạn có thể xây dựng:

  • Một sidebar quản trị responsive và collapsible
  • Tính năng làm nổi bật trạng thái hoạt động theo ngữ cảnh
  • Một mô hình có thể mở rộng cho cấu hình các mục menu

Cách tiếp cận này giúp điều hướng bảng điều khiển của bạn trở nên sạch sẽ, rõ ràng và bền vững cho tương lai. ⚡

Thực hành tốt nhất

  • Đảm bảo rằng sidebar có thể dễ dàng mở và đóng mà không làm ảnh hưởng đến trải nghiệm người dùng.
  • Sử dụng các biểu tượng dễ nhận biết để người dùng có thể nhanh chóng tìm thấy mục họ cần.
  • Kiểm tra tính tương thích trên các thiết bị di động và máy tính để bàn.

Những cạm bẫy thường gặp

  • Không cập nhật trạng thái sidebar khi người dùng thay đổi route.
  • Sử dụng quá nhiều biểu tượng có thể gây rối cho giao diện.

Mẹo hiệu suất

  • Tối ưu hóa các biểu tượng để giảm thời gian tải trang.
  • Sử dụng lazy loading cho các thành phần không cần thiết trong lần tải đầu tiên.

Khắc phục sự cố

  • Nếu sidebar không hiển thị đúng, hãy kiểm tra các quy tắc CSS và các lớp đã áp dụng.
  • Đảm bảo rằng useLocation được gọi trong một context của Router để có thể lấy được thông tin đúng.

Câu hỏi thường gặp

1. useLocation là gì và tại sao lại quan trọng?

useLocation là một hook trong React Router giúp bạn lấy thông tin về URL hiện tại, rất quan trọng để xác định trạng thái của các mục menu.

2. Làm thế nào để lưu trạng thái collapsible giữa các phiên làm việc?

Bạn có thể lưu trạng thái isCollapsed vào localStorage và phục hồi nó khi ứng dụng được khởi động lại.

3. Có những phương pháp nào để làm cho sidebar trở nên thân thiện hơn với người dùng?

Sử dụng biểu tượng rõ ràng, các lớp CSS để tạo hiệu ứng hover và đảm bảo rằng các mục menu được nhóm theo cách hợp lý.


Bài viết này đã cung cấp cho bạn một cái nhìn tổng quan về cách xây dựng một sidebar quản trị hiệu quả với React Router và useLocation. Hãy thử áp dụng vào dự án của bạn ngay hôm nay!

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào