如何修复react-router-dom v6中withRouter的listen功能失效问题
解决方案
问题核心在于用useState存储回调函数会在组件重渲染时丢失引用,且useState更新会触发不必要的重渲染。改用useRef持久化存储回调引用,同时完善listen的API兼容性,即可解决问题。
修改后的withRouter包装器代码
import * as React from 'react'; import { useLocation, useNavigate, useNavigationType, useParams } from "react-router"; export function withRouter(Child) { return (props) => { // 用useRef存储回调引用,持久化保存不会因重渲染丢失 const callBackRef = React.useRef(); const location = useLocation(); const navigate = useNavigate(); const action = useNavigationType(); const params = useParams(); const match = { params: params }; const history = { push: (url, onMount) => { if (onMount) { setTimeout(() => navigate(url), 100); } else { navigate(url); } }, listen: (callback) => { // 更新ref的current属性,无需触发组件重渲染 callBackRef.current = callback; // 返回取消监听的函数,对齐v5的API规范 return () => { callBackRef.current = undefined; }; } }; React.useEffect(() => { // 当location或action变化时,调用最新的回调 if (callBackRef.current) { callBackRef.current(location, action); } }, [location, action]); // 加入action依赖,对齐v5的监听逻辑 return <Child {...props} history={history} location={location} match={match} />; }; }
关键修改说明
- 用
useRef替代useState存储回调:useRef的.current属性在组件整个生命周期内保持引用,不会因组件重渲染被重置,彻底解决callBack变为undefined的问题,同时不会触发不必要的重渲染。 - 补充取消监听的返回函数:符合react-router-dom v5中
history.listen的API规范,允许组件在卸载时清理监听,避免内存泄漏。 - 调整
useEffect依赖:加入action参数,确保路由动作(如PUSH、POP)变化时也能触发回调,和v5的行为保持一致。
类组件的配套优化
为了避免重复监听和内存泄漏,建议在类组件中添加监听的清理逻辑:
componentDidMount(): void { // 保存取消监听的函数 this.unlisten = this.props.history.listen((location, action) => { const urlSearchParams = new URLSearchParams(window.location.search); const params = Object.fromEntries(urlSearchParams.entries()); if (params.id) { this.setState({id: params.id}); } }); this.getNotification(); Utils.scrollToTop(); } componentWillUnmount(): void { // 组件卸载时取消监听 if (this.unlisten) { this.unlisten(); } } // 可选:如果history对象发生变化,重新设置监听 componentDidUpdate(prevProps): void { if (prevProps.history !== this.props.history) { if (this.unlisten) { this.unlisten(); } this.unlisten = this.props.history.listen((location, action) => { const urlSearchParams = new URLSearchParams(window.location.search); const params = Object.fromEntries(urlSearchParams.entries()); if (params.id) { this.setState({id: params.id}); } }); } }
内容的提问来源于stack exchange,提问作者Majid M.
相关产品推荐
相关产品推荐

