You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Next.js侧边栏激活项与当前URL正确同步?

解决Next.js侧边栏激活项与URL同步问题

问题分析

你遇到的"NextRouter was not mounted"错误是因为组件初始化阶段过早访问路由对象,而侧边栏激活项不更新是因为仅通过点击事件维护状态,未监听路由变化(比如用户直接输入URL、刷新页面时状态无法同步)。

修复方案

通过路由映射+监听路由变化实现精准匹配,同时避免手动维护索引状态:

  1. 定义侧边栏菜单项的路由映射数组,统一管理路径、名称、图标等信息
  2. 在客户端组件中正确使用路由钩子获取当前路由
  3. 初始化时根据当前URL设置激活项,监听路由变化自动更新状态
  4. 移除手动点击设置状态的逻辑,让路由变化驱动状态更新

修改后的完整代码

"use client";
import React, { useState, useEffect } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import "@/app/globals.css";
import { Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";
import {
  Package,
  SquareArrowUpRight,
  HandCoins,
  Users,
  ScanEye,
  LayoutDashboard,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";

// 统一管理侧边栏菜单项
const sidebarItems = [
  { path: "/dashboard", label: "Dashboard", icon: <LayoutDashboard className="h-4 w-4" /> },
  { path: "/campaigns", label: "Campaign Manager", icon: <SquareArrowUpRight className="h-4 w-4" /> },
  { path: "/products", label: "Products", icon: <Package className="h-4 w-4" /> },
  { path: "/rewards-center", label: "Rewards Center", icon: <HandCoins className="h-4 w-4" /> },
  { path: "/customer-list", label: "Customer List", icon: <Users className="h-4 w-4" /> },
];

export default function Sidebar() {
  const pathname = usePathname();
  const { setTheme } = useTheme();

  const allItemClass = "flex items-center gap-3 rounded-lg px-3 py-2 transition-all";
  const activeItemClass = "bg-primary text-white dark:bg-primary dark:text-black";
  const inactiveItemClass = "text-muted-foreground hover:text-primary";

  // 初始化主题
  useEffect(() => {
    setTheme("system");
  }, [setTheme]);

  return (
    <div className="flex min-h-screen w-1/4">
      <div className="hidden border-r bg-muted/40 md:block">
        <div className="flex h-full max-h-screen flex-col gap-2">
          <div className="flex h-14 items-center border-b px-4 lg:h-[60px] lg:px-6">
            <Link
              href="/dashboard"
              className="flex items-center gap-2 font-semibold"
            >
              <ScanEye className="h-6 w-6" />
              <span className="">Reebews</span>
            </Link>
            <DropdownMenu>
              <DropdownMenuTrigger asChild>
                <Button
                  variant="outline"
                  size="icon"
                  className="ml-auto w-8 h-8"
                >
                  <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
                  <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
                  <span className="sr-only">Toggle theme</span>
                </Button>
              </DropdownMenuTrigger>
              <DropdownMenuContent align="end">
                <DropdownMenuItem onClick={() => setTheme("light")}>
                  Light
                </DropdownMenuItem>
                <DropdownMenuItem onClick={() => setTheme("dark")}>
                  Dark
                </DropdownMenuItem>
                <DropdownMenuItem onClick={() => setTheme("system")}>
                  System
                </DropdownMenuItem>
              </DropdownMenuContent>
            </DropdownMenu>
          </div>

          <div className="flex-1">
            <nav className="grid items-start px-2 text-sm font-medium lg:px-4">
              {/* 映射渲染侧边栏,自动匹配激活状态 */}
              {sidebarItems.map((item) => (
                <Link
                  key={item.path}
                  href={item.path}
                  className={`${allItemClass} ${
                    pathname === item.path ? activeItemClass : inactiveItemClass
                  }`}
                >
                  {item.icon}
                  {item.label}
                </Link>
              ))}
            </nav>
          </div>
          <div className="mt-auto p-4">
            <Card x-chunk="dashboard-02-chunk-0">
              <CardHeader className="p-2 pt-0 md:p-4">
                <CardTitle>Upgrade to Pro</CardTitle>
                <CardDescription>
                  Unlock all features and get unlimited access to our support
                  team.
                </CardDescription>
              </CardHeader>
              <CardContent className="p-2 pt-0 md:p-4 md:pt-0">
                <Button size="sm" className="w-full">
                  Upgrade
                </Button>
              </CardContent>
            </Card>
          </div>
        </div>
      </div>
    </div>
  );
}

关键修改说明

  • 路由映射数组:集中管理侧边栏项信息,避免硬编码索引,提升可维护性
  • usePathname钩子:Next.js App Router提供的客户端安全钩子,直接获取当前路由路径,避免"NextRouter was not mounted"错误
  • 自动匹配激活状态:通过pathname === item.path直接判断激活项,无需手动维护状态,路由变化时自动更新高亮
  • 移除手动状态设置:删除点击事件中的状态更新逻辑,完全由路由状态驱动侧边栏高亮,避免状态与路由不一致

内容的提问来源于stack exchange,提问作者Shreyans Jain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 08:29:54