如何在列表项内渲染Route,点击Link展开子组件?
优化你的路由展开列表组件
你的基础方案思路是对的,但大概率会遇到两个常见问题:一是未激活的<li>里的Route其实也会被挂载(只是不渲染内容),如果Topic组件比较复杂会有性能损耗;二是缺少视觉上的展开反馈,用户点击后不知道哪个项是当前激活的。
我给你两种优化思路,结合React Router的Hooks来实现更优雅的效果:
方案一:用路由参数控制激活状态与组件渲染
这种方式更轻量化,直接通过路由参数判断哪个项需要展开并渲染子组件:
import { Link, Route, useRouteMatch, useParams } from 'react-router-dom'; const Topics = () => { // 获取当前路由的path和url,替代props传递的match const { path, url } = useRouteMatch(); // 获取路由中的topic路径参数(需要你在父路由里定义为/:topicPath) const { topicPath } = useParams(); return ( <div> <h2>Topics</h2> <ul> {topics.map(topic => { // 判断当前项是否为激活状态 const isExpanded = topic.path === topicPath; return ( <li key={topic.id} // 给激活项加样式类,控制展开效果 className={isExpanded ? 'topic-item expanded' : 'topic-item'} > <Link to={`${url}/${topic.path}`}>{topic.title}</Link> {/* 只有激活时才渲染Topic组件,减少不必要的组件挂载 */} {isExpanded && <Topic topic={topic} />} </li> ); })} </ul> </div> ); };
方案二:保留Route但配合render prop(更符合路由驱动思想)
如果你更倾向于用Route来控制组件渲染,可以用render prop传递数据,同时结合路由匹配判断激活状态:
import { Link, Route, useRouteMatch, useLocation } from 'react-router-dom'; const Topics = () => { const { path, url } = useRouteMatch(); const location = useLocation(); return ( <div> <h2>Topics</h2> <ul> {topics.map(topic => { const topicRoutePath = `${path}/${topic.path}`; const isExpanded = location.pathname === topicRoutePath; return ( <li key={topic.id} className={isExpanded ? 'topic-item expanded' : 'topic-item'} > <Link to={`${url}/${topic.path}`}>{topic.title}</Link> {/* 用render prop传递topic数据,避免组件不必要的重新挂载 */} <Route path={topicRoutePath} render={() => <Topic topic={topic} />} /> </li> ); })} </ul> </div> ); };
加上展开样式(关键视觉反馈)
不管用哪种方案,都需要CSS来实现展开的动画效果,比如:
.topic-item { max-height: 2.5rem; /* 默认只显示链接的高度 */ overflow: hidden; transition: max-height 0.3s ease-in-out; border-bottom: 1px solid #eee; padding: 0.5rem; } .topic-item.expanded { max-height: 20rem; /* 设置足够容纳子组件的高度 */ background-color: #f8f9fa; }
为什么原有方案需要优化?
你的初始代码虽然能运行,但:
- 所有
<li>里的Route都会被初始化,哪怕路由不匹配,Topic组件的构造函数/useEffect都会执行,浪费性能; - 没有激活状态的样式,用户无法直观看到哪个项是当前展开的;
- 用
componentprop传递的话,每次路由匹配都会重新创建Topic组件实例,而用renderprop可以避免这个问题。
内容的提问来源于stack exchange,提问作者ColeCat
相关产品推荐
相关产品推荐

