React Router点击当前页面菜单时如何避免页面重渲染?
问题背景
我没找到类似问题的解答,想确认以下需求是否可实现:为优化性能,点击菜单栏中当前所在页面的链接时,能否避免页面刷新/重渲染?
当前使用的路由配置如下:
<Routes> <Route path='/' element={<Login />} /> <Route element={<WebsiteBars />}> <Route path='/dashboard' element={<Dashboard />} /> <Route path='/table' element={<Table/>} /> <Route path='/settings' element={<UserSettings />} /> <Route path='/help' element={<HelpPage />} /> </Route> </Routes>
菜单栏、应用栏、页脚等均封装在WebsiteBars组件中,每个菜单项基于Link组件实现(如to="/settings")。目前遇到的问题是:当处于Table页面时,再次点击菜单中的Table链接,页面会触发重渲染,且每次进入页面时生成的假数据会新增更多行——这是我希望避免的行为。
完整的App.js结构:
<VesselProvider> <Routes> <Route path='/' element={<Login />} /> <Route element={<WebsiteBars />}> <Route path='/dashboard' element={<Dashboard />} /> <Route path='/table' element={<Table/>} /> <Route path='/settings' element={<UserSettings />} /> <Route path='/help' element={<HelpPage />} /> </Route> </Routes> </VesselProvider>
其中VesselProvider是自定义的上下文提供者。
涉及的Table组件代码:
DefaultTable组件
const DefaultTable = ({ data }) => { // 包含用于填充表格数据的useEffect等逻辑 const { setRowData } = useVessel(); // 从VesselProvider获取上下文 return <Table> ... </Table>; // 普通MUI Table组件,使用传入的data }
假数据生成逻辑
const rows = []; const randSize = faker.datatype.number({ min: 10, max: 150 }); function fakeData() { for (let i = 0; i < randSize; i++) { rows.push( { status: faker.helpers.arrayElement(['sent', 'waiting', 'missing', 'resolved', 'read', 'created', 'modified']), ID: faker.random.numeric(6), filename: `${randTitle}.${faker.helpers.arrayElement(['xml', 'pptx'])}`, filesize: faker.datatype.number({ min: 10, max: 200 }) + "KB", subject: faker.helpers.arrayElement(['Invoice', 'Order']), } ); } return rows; } const Table=()=>{ return <DefaultTable data={fakeData()} /> }
核心疑问
- 能否让当前所在路径不触发重渲染?有没有办法告知路由不再执行路由操作,或在处于链接指向的同一路径时“禁用”该链接?
- 如果改用真实后端API获取数据,能否避免这种“异常行为”?
解决方案
1. 禁用当前路径的菜单链接
通过useLocation钩子获取当前路由路径,对比菜单项的to属性,当路径匹配时,给Link添加禁用样式并阻止点击交互:
import { Link, useLocation } from 'react-router-dom'; const MenuItem = ({ to, label }) => { const location = useLocation(); const isActive = location.pathname === to; return ( <Link to={to} style={{ pointerEvents: isActive ? 'none' : 'auto', opacity: isActive ? 0.6 : 1 }} > {label} </Link> ); };
2. 阻止同路径的路由跳转
若不想修改样式,可自定义Link组件,在点击时判断当前路径与目标路径是否一致,一致则阻止默认跳转行为:
import { Link, useLocation, useNavigate } from 'react-router-dom'; const CustomLink = ({ to, children }) => { const location = useLocation(); const navigate = useNavigate(); const handleClick = (e) => { if (location.pathname === to) { e.preventDefault(); // 阻止默认路由跳转 } }; return ( <Link to={to} onClick={handleClick}> {children} </Link> ); };
3. 修复Table组件的数据生成逻辑
你遇到的“新增更多行”问题,本质是全局变量rows被重复添加数据,而非路由重渲染直接导致。即使不触发路由跳转,只要组件重新渲染,fakeData()就会往全局rows数组里新增数据。
修改假数据生成逻辑,将rows移到函数内部,并使用useMemo缓存数据:
function fakeData() { const rows = []; // 每次调用都创建新数组,避免全局污染 const randSize = faker.datatype.number({ min: 10, max: 150 }); for (let i = 0; i < randSize; i++) { rows.push( { status: faker.helpers.arrayElement(['sent', 'waiting', 'missing', 'resolved', 'read', 'created', 'modified']), ID: faker.random.numeric(6), filename: `${randTitle}.${faker.helpers.arrayElement(['xml', 'pptx'])}`, filesize: faker.datatype.number({ min: 10, max: 200 }) + "KB", subject: faker.helpers.arrayElement(['Invoice', 'Order']), } ); } return rows; } const Table=()=>{ // 空依赖数组,仅在组件挂载时生成一次数据 const tableData = useMemo(() => fakeData(), []); return <DefaultTable data={tableData} /> }
关于真实API的说明
改用真实后端API后,若你的数据获取逻辑是在组件挂载时调用一次(例如使用useEffect且依赖为空数组),那么即使路由触发重渲染,只要组件未被卸载再挂载,就不会重复请求数据。
结合前面的路由优化方案,阻止同路径的路由跳转后,组件不会因点击当前链接而重新渲染,自然也不会重复发起API请求。
内容的提问来源于stack exchange,提问作者Pompompurin

