React Router v6 路由变化监听
React Router v6 引入了许多重大变更,其中一个常见问题是如何监听路由变化。与 v5 版本中使用 useHistory
的方式不同,v6 提供了新的 API 来处理路由导航和监听。
问题概述
在 React Router v5 中,开发者可以使用 useHistory
hook 来获取历史记录对象,并通过其 listen
方法监听路由变化:
// React Router v5
const history = useHistory();
history.listen((location, action) => {
// 路由变化处理逻辑
});
但在 v6 版本中,useHistory
被替换为 useNavigate
,而新的 navigate
函数不再提供 listen
方法:
// React Router v6
const navigate = useNavigate();
navigate.listen(...); // ❌ listen 不是函数
解决方案
方法一:使用 useLocation Hook(推荐)
最简单且最常用的方法是使用 useLocation
hook 配合 useEffect
:
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
function MyComponent() {
const location = useLocation();
useEffect(() => {
// 当 location 变化时执行
console.log('路由已变更:', location.pathname);
// 可以在这里添加 analytics 跟踪等逻辑
ga('send', 'pageview');
}, [location]); // location 作为依赖项
return <div>当前路径: {location.pathname}</div>;
}
TIP
这种方法适用于大多数常见场景,特别是当您只需要在组件挂载时响应路由变化的情况。
方法二:监听特定导航动作
如果需要区分不同的导航类型(如浏览器前进/后退按钮),可以使用 useNavigationType
:
import { useEffect } from 'react';
import { useLocation, useNavigationType } from 'react-router-dom';
function useBackListener(callback) {
const location = useLocation();
const navType = useNavigationType();
useEffect(() => {
if (navType === 'POP' && location.key !== 'default') {
// 处理浏览器后退/前进按钮点击
callback();
}
}, [location, navType, callback]);
}
// 使用示例
function MyComponent() {
useBackListener(() => {
console.log('用户点击了浏览器后退或前进按钮');
});
return <div>组件内容</div>;
}
方法三:使用自定义 History 对象(高级)
对于需要更精细控制的场景,可以创建自定义历史记录对象:
// 首先安装 history 库
// npm install history@5
// myHistory.js
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
export default history;
// CustomRouter.js
import { useState, useLayoutEffect } from 'react';
import { Router } from 'react-router-dom';
const CustomRouter = ({ history, ...props }) => {
const [state, setState] = useState({
action: history.action,
location: history.location
});
useLayoutEffect(() => history.listen(setState), [history]);
return (
<Router
{...props}
location={state.location}
navigationType={state.action}
navigator={history}
/>
);
};
export default CustomRouter;
// App.js
import CustomRouter from './CustomRouter';
import history from './myHistory';
function App() {
return (
<CustomRouter history={history}>
{/* 您的路由配置 */}
</CustomRouter>
);
}
// 在组件中使用
import { useEffect } from 'react';
import history from './myHistory';
function MyComponent() {
useEffect(() => {
const unlisten = history.listen(({ location, action }) => {
console.log(`路由变化: ${action} -> ${location.pathname}`);
});
return unlisten; // 清理函数
}, []);
return <div>组件内容</div>;
}
WARNING
React Router v6.4+ 提供了 unstable_HistoryRouter
,但这仍被标记为"不稳定",可能在将来版本中发生变化。
方法四:使用 Data Router 的订阅功能(v6.4+)
如果您使用 React Router v6.4+ 的数据路由器:
import { createBrowserRouter } from 'react-router-dom';
const router = createBrowserRouter([...]);
// 监听路由状态变化
router.subscribe((state) => {
console.log('路由状态变更:', state);
});
// 导航到新路径
router.navigate('/path');
// 替换当前历史记录
router.navigate('/path', { replace: true });
最佳实践建议
优先使用
useLocation
:对于大多数用例,useLocation
+useEffect
是最简单可靠的解决方案。注意组件卸载:如果您的方法涉及在 useEffect 中设置监听器,请确保返回清理函数以避免内存泄漏。
谨慎使用不稳定 API:避免在生产环境中使用标记为
unstable_
的 API,因为它们可能在未来的版本中发生变化。考虑性能影响:路由变化监听可能会频繁触发,确保您的处理逻辑是高效的。
总结
React Router v6 提供了多种方式来监听路由变化,从简单的 useLocation
hook 到高级的自定义历史记录对象。根据您的具体需求选择合适的方法,并遵循 React 的最佳实践来确保代码的可靠性和性能。
INFO
本文基于 React Router v6.8.0,请注意不同版本间可能存在 API 差异,建议查阅官方文档获取最新信息。