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

如何避免React路由切换时SideBarLink组件重复渲染?

解决React路由切换时SideBarLink重复渲染的问题

问题背景

在React项目中,SideBar组件通过循环SIDEBAR_LINKS数组渲染多个SideBarLink组件,首次加载时渲染次数与数组长度一致,但每次路由切换时,所有SideBarLink都会重复渲染。尝试使用React.memo优化,但未生效,需要在保留单独文件定义路由的前提下解决该问题。

原组件代码(参考)

SideBarLink组件

import React from "react";
import { Link, useLocation } from "react-router-dom";
import classNames from "classnames";
import { SideBarRouteLink } from "../../modules/Routes/SideBarRoutes";

const linkClass =
  "flex items-center gap-2 font-light px-3 py-2 hover:text-white hover:font-bold rounded-sm text-base";

interface SideBarLinkProps {
  link: SideBarRouteLink;
}

const SideBarLink: React.FC<SideBarLinkProps> = ({ link }) => {
  console.log("SideBarLink");

  const { pathname } = useLocation();

  return (
    <Link
      to={link.path}
      className={classNames(
        "flex items-center relative",
        pathname === link.path ? "text-white font-bold" : "text-slate-100",
        linkClass
      )}
    >
      {pathname === link.path && (
        <span className="absolute top-1/2 left-0 transform -translate-y-1/2 w-1 h-6 bg-white"></span>
      )}
      <div className="flex items-center gap-2">
        <span className="text-xl">
          <link.icon />
        </span>
        <span>{link.label}</span>
      </div>
    </Link>
  );
};

export default SideBarLink;

SideBar组件

import React from "react";
import { SIDEBAR_LINKS } from "../../modules/Routes/SideBarRoutes";
import SideBarLink from "./SideBarLink";

const SideBarView: React.FC = () => {
  console.log("SideBar VIEEEW");

  return (
    <div className="bg-gradient-to-br from-blue-600 to-cyan-400 w-60 p-3 flex flex-col">
      <div className="flex gap-2 justify-center px-1 py-8">
        <span className="font-medium leading-10 tracking-widest text-white text-2xl">
          KAROOOBAR
        </span>
      </div>
      <div className="border-t-2 border-b-2 border-gray-100 py-4 text-center">
        <h1 className="text-base text-white">Sample Store</h1>
      </div>
      <div className="flex flex-col gap-0.5 py-8 ">
        {SIDEBAR_LINKS.map((link) => (
          <SideBarLink key={link.key} link={link} />
        ))}
      </div>
      <div className="flex flex-col gap-4 px-3">
        <a
          href="mailto:karooobar@gmail.com"
          className="font-light text-white text-sm"
        >
          karooobar@gmail.com
        </a>
        <p className="font-light text-white text-sm">© 2023, Karooobar Inc.</p>
      </div>
    </div>
  );
};

export default SideBarView;

问题原因

  • SideBarLink内部使用useLocation钩子,路由切换时location对象更新,会触发所有依赖该钩子的组件重新渲染,React.memo默认仅对比props变化,无法拦截context/钩子依赖变化导致的重渲染。
  • 自定义的memo比较函数仅对比link对象引用,未考虑路由变化对组件渲染的实际影响——只有当当前路由与该link的path匹配关系改变时,组件才需要重新渲染,其他路由切换无需触发。

解决方案

步骤1:将路由状态提升到父组件

在SideBarView中获取当前pathname,作为props传递给每个SideBarLink,让SideBarLink不再直接依赖路由context。

修改后的SideBarView代码:

import React from "react";
import { useLocation } from "react-router-dom";
import { SIDEBAR_LINKS } from "../../modules/Routes/SideBarRoutes";
import SideBarLink from "./SideBarLink";

const SideBarView: React.FC = () => {
  console.log("SideBar VIEEEW");
  const { pathname } = useLocation();

  return (
    <div className="bg-gradient-to-br from-blue-600 to-cyan-400 w-60 p-3 flex flex-col">
      <div className="flex gap-2 justify-center px-1 py-8">
        <span className="font-medium leading-10 tracking-widest text-white text-2xl">
          KAROOOBAR
        </span>
      </div>
      <div className="border-t-2 border-b-2 border-gray-100 py-4 text-center">
        <h1 className="text-base text-white">Sample Store</h1>
      </div>
      <div className="flex flex-col gap-0.5 py-8 ">
        {SIDEBAR_LINKS.map((link) => (
          <SideBarLink key={link.key} link={link} currentPath={pathname} />
        ))}
      </div>
      <div className="flex flex-col gap-4 px-3">
        <a
          href="mailto:karooobar@gmail.com"
          className="font-light text-white text-sm"
        >
          karooobar@gmail.com
        </a>
        <p className="font-light text-white text-sm">© 2023, Karooobar Inc.</p>
      </div>
    </div>
  );
};

export default SideBarView;

步骤2:修改SideBarLink组件,优化React.memo逻辑

移除SideBarLink内部的useLocation,接收currentPath作为props,并优化memo比较函数——仅当组件的激活状态发生变化时才重新渲染。

修改后的SideBarLink代码:

import React from "react";
import { Link } from "react-router-dom";
import classNames from "classnames";
import { SideBarRouteLink } from "../../modules/Routes/SideBarRoutes";

const linkClass =
  "flex items-center gap-2 font-light px-3 py-2 hover:text-white hover:font-bold rounded-sm text-base";

interface SideBarLinkProps {
  link: SideBarRouteLink;
  currentPath: string;
}

const SideBarLink: React.FC<SideBarLinkProps> = React.memo(
  ({ link, currentPath }) => {
    console.log(`SideBarLink: ${link.label}`);

    return (
      <Link
        to={link.path}
        className={classNames(
          "flex items-center relative",
          currentPath === link.path ? "text-white font-bold" : "text-slate-100",
          linkClass
        )}
      >
        {currentPath === link.path && (
          <span className="absolute top-1/2 left-0 transform -translate-y-1/2 w-1 h-6 bg-white"></span>
        )}
        <div className="flex items-center gap-2">
          <span className="text-xl">
            <link.icon />
          </span>
          <span>{link.label}</span>
        </div>
      </Link>
    );
  },
  (prevProps, nextProps) => {
    // 判断条件:link引用不变,且组件激活状态未改变
    const wasActive = prevProps.currentPath === prevProps.link.path;
    const isActive = nextProps.currentPath === nextProps.link.path;
    return prevProps.link === nextProps.link && wasActive === isActive;
  }
);

SideBarLink.displayName = "SideBarLink";

export default SideBarLink;

效果说明

修改后,路由切换时仅之前激活和当前激活的两个SideBarLink会重新渲染,其他未涉及状态变化的组件会被React.memo跳过,彻底避免不必要的重复渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 01:07:05