React Router中使用history.push时如何避免组件重复渲染?
核心原因
当前组件重渲染重复触发查询的根因是:MyComponent绑定了RouteComponentProps,路由切换时react-router会更新props触发组件重渲染,而高开销查询直接写在组件内部没有做缓存或重渲染拦截,因此每次返回都会重新执行。
解决方案
方案1:组件拆分 + 查询缓存(最推荐)
将高开销的列表逻辑抽离为独立组件,配合请求库的缓存能力避免重复请求:
- 若使用
React Query/SWR这类请求库,只要给查询设置合理的staleTime,相同参数的查询会直接读缓存不会重发请求,不需要额外调整组件结构。 - 组件拆分示例代码:
// 抽离独立的列表组件,不需要从上层接收history props const ExpensiveList: React.FC = () => { // 高开销查询移到该组件内部 const {data, loading, error} = someExpensiveQuery({/* 原查询参数 */}); // 内部通过useHistory获取实例,避免props传递带来的重渲染 const history = useHistory(); return ( <div> {/* 原海量列表渲染逻辑,点击跳转用内部history.push */} </div> ) } // 外层路由组件不再接收RouteComponentProps const MyComponent: React.FC = () => { return( <Switch> <Route path='/somePath' exact> <ExpensiveList /> </Route> <Route path='/somePath/edit/:id'> {/* 原编辑组件逻辑 */} </Route> </Switch> ) }
方案2:用React.memo拦截不必要重渲染
如果请求本身没有缓存能力,可以给抽离后的列表组件加React.memo做浅层props比较,抽离后的列表组件没有会变化的props,路由切换时不会被重渲染:
const ExpensiveList = React.memo(() => { const {data, loading, error} = someExpensiveQuery({/* 原查询参数 */}); // 其他列表逻辑 })
方案3:固定查询依赖项
如果不想拆分组件,可以调整查询的触发条件,只要查询参数没有变化就不重新执行:
- 若用useEffect封装原生请求,将请求的依赖项设为空数组,只会在组件首次挂载时触发
- 若用React Query,给查询设置
staleTime: Infinity,除非手动触发失效,否则永远不会重新请求
内容的提问来源于stack exchange,提问作者Harsha Limaye
相关产品推荐
相关产品推荐

