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

React侧边栏点击链接组件不渲染问题(React Router v6)

React Router v6 侧边栏组件无法渲染问题解决

开发带侧边栏的React管理面板时,点击侧边栏链接后对应组件无法正常显示。此前参考的是React Router v5方案,当前使用v6版本,相关代码如下:

原代码

App.jsx

function App() {
  return (
    <>
      <BrowserRouter>
        <Header />
        <Routes>
          <Route path="/" index element={<Home />} />
          <Route path="/login" element={<Login />} />
          <Route path="/adminPanel" element={<AdminPanel />} />
          <Route path="/dashboard" element={<Dashboard />} />
          <Route path="/scanNow" element={<ScanNow />} />
          <Route path="/scanHistory" element={<ScanHistory />} />
          <Route path="/users" element={<Users />} />
          <Route path="/profile" element={<Profile />} />
          <Route path="/settings" element={<Settings />} />
        </Routes>
      </BrowserRouter>
    </>
  )
}

export default App

AdminPanel.jsx

export default function AdminPanel() {
  const [selectedComponent, setSelectedComponent] = useState("");

  const location = useLocation();
  // const isLoggedIn = location.pathname.includes('adminPanel');

  return (
    <>
      <div className="adminPanel">
        <div className="adminPanel__container">
          <SideBar />
          <div>
            {selectedComponent === 'dashboard' && <Dashboard />}
            {selectedComponent === 'scanNow' && <ScanNow />}
            {selectedComponent === 'scanHistory' && <ScanHistory />}
            {selectedComponent === 'users' && <Users />}
            {selectedComponent === 'profile' && <Profile />}
            {selectedComponent === 'settings' && <Settings />}
          </div>
        </div>
      </div>
    </>
  )
}
export default function SideBar() {
  const [showMobSidebar, setShowMobSidebar] = useState(false);
  const [activeLink, setActiveLink] = useState("");

  // useEffect to show the mobile sidebar when the component mounts and event
  // listener is added and removed
  useEffect(() => {
    setShowMobSidebar(window.innerWidth < 767);

    const handleResize = () => {
      setShowMobSidebar(window.innerWidth < 767);
    };

    window.addEventListener("resize", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);

  // handle the click for the active link
  const handleLinkClick = (link) => {
    setActiveLink(link);
  };

  return (
    <>
      {showMobSidebar ? (
        <MobileSidebar />
      ) : (
        <div className="sidebar">
          <ul className="sidebar__lists">
            <li className="sidebar__lists-item">
              <Link
                className={`sidebar__lists-link ${activeLink === "dashboard" ? "active" : ""}`}
                to="/dashboard"
                onClick={() => handleLinkClick("dashboard")}
              >
                <MdDashboard className="react-icons" />
                Dashboard
              </Link>
            </li>
            <li className="sidebar__lists-item">
              <Link
                className={`sidebar__lists-link ${activeLink === "scanNow" ? "active" : ""}`}
                to="/scanNow"
                onClick={() => handleLinkClick("scanNow")}
              >
                <CgPhotoscan className="react-icons" />
                Scan Now
              </Link>
            </li>
            <li className="sidebar__lists-item">
              <Link
                className={`sidebar__lists-link ${activeLink === "scanHistory" ? "active" : ""}`}
                to="/scanHistory"
                onClick={() => handleLinkClick("scanHistory")}
              >
                <MdHistory className="react-icons" />
                Scan History
              </Link>
            </li>
            <li className="sidebar__lists-item">
              <Link
                className={`sidebar__lists-link ${activeLink === "users" ? "active" : ""}`}
                to="/users"
                onClick={() => handleLinkClick("users")}
              >
                <HiOutlineUsers className="react-icons" />
                Users
              </Link>
            </li>
            <li className="sidebar__lists-item">
              <Link
                className={`sidebar__lists-link ${activeLink === "profile" ? "active" : ""}`}
                to="/profile"
                onClick={() => handleLinkClick("profile")}
              >
                <CgProfile className="react-icons" />
                Profile
              </Link>
            </li>
            <li className="sidebar__lists-item">
              <Link
                className={`sidebar__lists-link ${activeLink === "settings" ? "active" : ""}`}
                to="/settings"
                onClick={() => handleLinkClick("settings")}
              >
                <MdOutlineSettings className="react-icons" />
                Settings
              </Link>
            </li>
            <li className="sidebar__lists-item">
              <Link
                className={`sidebar__lists-link ${activeLink === "logout" ? "active" : ""}`}
                to="/login"
                onClick={() => handleLinkClick("logout")}
              >
                <MdOutlineLogout className="react-icons" />
                Logout
              </Link>
            </li>
          </ul>
        </div>
      )}
    </>
  );
}

问题分析

  1. 路由结构冲突:/dashboard、/scanNow等是顶级路由,和/adminPanel平级。点击侧边栏的Link会跳转到这些顶级路由,导致AdminPanel组件被卸载,其内部通过selectedComponent控制的组件无法渲染。
  2. 状态不同步:AdminPanel的selectedComponent状态从未被更新,侧边栏的handleLinkClick仅维护自身的activeLink,未同步到父组件。

解决方案:使用React Router v6嵌套路由

通过嵌套路由让AdminPanel作为父路由,子路由组件在其内部渲染,同时利用路由自动同步激活状态。

修改后的代码

1. App.jsx(配置嵌套路由)

function App() {
  return (
    <>
      <BrowserRouter>
        <Header />
        <Routes>
          <Route path="/" index element={<Home />} />
          <Route path="/login" element={<Login />} />
          {/* 将AdminPanel设为父路由,嵌套子路由 */}
          <Route path="/adminPanel" element={<AdminPanel />}>
            <Route index element={<Dashboard />} /> {/* 默认显示Dashboard */}
            <Route path="dashboard" element={<Dashboard />} />
            <Route path="scanNow" element={<ScanNow />} />
            <Route path="scanHistory" element={<ScanHistory />} />
            <Route path="users" element={<Users />} />
            <Route path="profile" element={<Profile />} />
            <Route path="settings" element={<Settings />} />
          </Route>
        </Routes>
      </BrowserRouter>
    </>
  )
}

export default App

2. AdminPanel.jsx(添加Outlet渲染子路由)

import { Outlet } from 'react-router-dom';

export default function AdminPanel() {
  return (
    <>
      <div className="adminPanel">
        <div className="adminPanel__container">
          <SideBar />
          {/* 子路由组件会渲染在此处 */}
          <Outlet />
        </div>
      </div>
    </>
  )
}

3. SideBar.jsx(更新路径并自动同步激活状态)

import { Link, useLocation } from 'react-router-dom';

export default function SideBar() {
  const [showMobSidebar, setShowMobSidebar] = useState(false);
  const location = useLocation();

  useEffect(() => {
    setShowMobSidebar(window.innerWidth < 767);

    const handleResize = () => {
      setShowMobSidebar(window.innerWidth < 767);
    };

    window.addEventListener("resize", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);

  // 根据当前路径判断链接是否激活
  const isActive = (path) => {
    return location.pathname.includes(path);
  };

  return (
    <>
      {showMobSidebar ? (
        <MobileSidebar />
      ) : (
        <div className="sidebar">
          <ul className="sidebar__lists">
            <li className="sidebar__lists-item">
              <Link
                className={`sidebar__lists-link ${isActive("dashboard") ? "active" : ""}`}
                to="/adminPanel/dashboard"
              >
                <MdDashboard className="react-icons" />
                Dashboard
              </Link>
            </li>
            <li className="sidebar__lists-item">
              <Link
                className={`sidebar__lists-link ${isActive("scanNow") ? "active" : ""}`}
                to="/adminPanel/scanNow"
              >
                <CgPhotoscan className="react-icons" />
                Scan Now
              </Link>
            </li>
            <li className="sidebar__lists-item">
              <Link
                className={`sidebar__lists-link ${isActive("scanHistory") ? "active" : ""}`}
                to="/adminPanel/scanHistory"
              >
                <MdHistory className="react-icons" />
                Scan History
              </Link>
            </li>
            <li className="sidebar__lists-item">
              <Link
                className={`sidebar__lists-link ${isActive("users") ? "active" : ""}`}
                to="/adminPanel/users"
              >
                <HiOutlineUsers className="react-icons" />
                Users
              </Link>
            </li>
            <li className="sidebar__lists-item">
              <Link
                className={`sidebar__lists-link ${isActive("profile") ? "active" : ""}`}
                to="/adminPanel/profile"
              >
                <CgProfile className="react-icons" />
                Profile
              </Link>
            </li>
            <li className="sidebar__lists-item">
              <Link
                className={`sidebar__lists-link ${isActive("settings") ? "active" : ""}`}
                to="/adminPanel/settings"
              >
                <MdOutlineSettings className="react-icons" />
                Settings
              </Link>
            </li>
            <li className="sidebar__lists-item">
              <Link
                className={`sidebar__lists-link ${isActive("login") ? "active" : ""}`}
                to="/login"
              >
                <MdOutlineLogout className="react-icons" />
                Logout
              </Link>
            </li>
          </ul>
        </div>
      )}
    </>
  );
}

关键修改点

  • 嵌套路由配置:将Dashboard等组件设为/adminPanel的子路由,确保点击链接时AdminPanel保持挂载。
  • Outlet组件:替代原有的条件渲染逻辑,专门用于渲染子路由组件。
  • 自动激活状态:通过useLocation获取当前路径,自动判断链接是否激活,无需手动维护状态。
  • 路径更新:侧边栏链接路径改为/adminPanel/xxx,匹配嵌套路由结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 07:35:16