React高阶组件(HOC)中如何使用history.push?Axios拦截器跳转问题求助
我来帮你解决这个Axios拦截器HOC里无法跳转页面的问题~
问题原因分析
你的HOC里拿到的props是空对象,核心原因是:被HOC包裹的App组件没有直接被react-router的<Route>组件渲染,所以它不会自动获得history、location这些路由相关的props,导致HOC里拿不到props.history。
另外还要注意一个隐藏坑:你的useEffect没有清理逻辑,每次组件挂载都会新增一个Axios拦截器,多次挂载后会出现重复执行错误处理逻辑的问题,这个后面会一起解决。
解决方案
下面给你三种可行的解决方式,你可以根据项目的React Router版本选择:
方案1:用withRouter包装HOC(React Router v5及以下适用)
从react-router-dom导入withRouter,它可以给组件注入路由相关的props。你可以选择包装HOC内部的组件,或者包装最终导出的App组件:
修改HOC代码:
import React, { useEffect } from "react"; import axios from "axios"; import { withRouter } from "react-router-dom"; // 导入withRouter const checkRequests = Wrapped => { function CheckRequests(props) { useEffect(()=>{ // 保存拦截器实例,方便后续清理 const interceptor = axios.interceptors.response.use( response => response, error => { if (error.response?.status === 404) { props.history.push('/login'); store.dispatch(logout()); // 执行你的登出action } return Promise.reject(error); } ); // 组件卸载时移除拦截器,避免重复添加 return () => axios.interceptors.response.eject(interceptor); }, [props.history]); // 依赖history,避免不必要的重渲染 return <Wrapped {...props} />; } // 用withRouter包装CheckRequests,注入路由props return withRouter(CheckRequests); }; export default checkRequests;
或者修改App.js的导出:
import React from "react" import CheckRequests from "./HOC/CheckRequests" import { withRouter } from "react-router-dom"; function App(props){ // ...你的组件逻辑 } // 用withRouter包裹最终的组件 export default withRouter(checkRequests(App));
方案2:使用useNavigate/useHistory钩子(React Router v6+推荐)
React Router v6移除了withRouter,推荐使用路由钩子。直接在HOC的函数组件里使用useNavigate(v6)或useHistory(v5):
React Router v6版本代码:
import React, { useEffect } from "react"; import axios from "axios"; import { useNavigate } from "react-router-dom"; // v6用useNavigate const checkRequests = Wrapped => { function CheckRequests(props) { const navigate = useNavigate(); // 获取导航方法 useEffect(()=>{ const interceptor = axios.interceptors.response.use( response => response, error => { if (error.response?.status === 404) { navigate('/login'); // 跳转到登录页 store.dispatch(logout()); } return Promise.reject(error); } ); return () => axios.interceptors.response.eject(interceptor); }, [navigate]); // 依赖navigate return <Wrapped {...props} />; } return CheckRequests; }; export default checkRequests;
方案3:创建全局history实例(无版本限制)
如果你不想依赖组件的props或钩子,可以自己创建一个全局的history实例,在路由配置和拦截器里共用:
- 先安装
history包(React Router v6已内置,v5需要手动安装):
npm install history
- 创建
history.js文件:
// history.js import { createBrowserHistory } from 'history'; export const history = createBrowserHistory();
- 修改路由配置(比如
index.js):
import { Router } from 'react-router-dom'; import { history } from './history'; ReactDOM.render( <Router history={history}> <App /> </Router>, document.getElementById('root') );
- 在HOC里直接使用全局history:
import React, { useEffect } from "react"; import axios from "axios"; import { history } from '../history'; // 导入全局history const checkRequests = Wrapped => { function CheckRequests(props) { useEffect(()=>{ const interceptor = axios.interceptors.response.use( response => response, error => { if (error.response?.status === 404) { history.push('/login'); store.dispatch(logout()); } return Promise.reject(error); } ); return () => axios.interceptors.response.eject(interceptor); }, []); return <Wrapped {...props} />; } return CheckRequests; }; export default checkRequests;
内容的提问来源于stack exchange,提问作者SDK
相关产品推荐
相关产品推荐

