如何让Next.js侧边栏激活项与当前URL正确同步?
解决Next.js侧边栏激活项与URL同步问题
问题分析
你遇到的"NextRouter was not mounted"错误是因为组件初始化阶段过早访问路由对象,而侧边栏激活项不更新是因为仅通过点击事件维护状态,未监听路由变化(比如用户直接输入URL、刷新页面时状态无法同步)。
修复方案
通过路由映射+监听路由变化实现精准匹配,同时避免手动维护索引状态:
- 定义侧边栏菜单项的路由映射数组,统一管理路径、名称、图标等信息
- 在客户端组件中正确使用路由钩子获取当前路由
- 初始化时根据当前URL设置激活项,监听路由变化自动更新状态
- 移除手动点击设置状态的逻辑,让路由变化驱动状态更新
修改后的完整代码
"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
相关产品推荐
相关产品推荐

