React中Axios请求头JWT未更新 需手动重载页面问题
问题表现
登录流程中拿到新JWT令牌写入localStorage、更新Axios默认请求头后,通过React Router跳转到首页,首页加载时自动发起的POST请求携带的仍是旧令牌,只有手动刷新整个页面后,请求才会携带新令牌。
相关代码:
登录页逻辑(login.tsx)
// ... const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); const data = new FormData(event.currentTarget); axiosInstance .post('token/', { username: data.get('username'), password: data.get('password'), }) .then((res) => { localStorage.setItem('access_token', res.data.access) localStorage.setItem('refresh_token', res.data.refresh) axiosInstance.defaults.headers.common['Authorization'] = 'JWT ' + localStorage.getItem('access_token') navigate('/', { replace: true }) }) } // ...
首页逻辑(home.tsx)
// ... const updateData = () => { return axiosInstance.post(`words/update/`, { 'timezone': Intl.DateTimeFormat().resolvedOptions().timeZone }) } useEffect(() => { updateData() }, []) // ...
路由配置(App.tsx)
import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import AddWord from "./components/addWord"; import './App.css'; import Home from "./components/home"; import SignIn from "./components/signIn"; import SignUp from "./components/signUp"; function App() { return ( <div className="container"> <Router> <Routes> <Route path="/" element={<Home />} /> <Route path="/add_word" element={<AddWord />} /> <Route path="/login" element={<SignIn />} /> <Route path="/register" element={<SignUp />} /> </Routes> </Router> </div> ); } export default App;
问题原因
直接修改axiosInstance.defaults.headers的令牌注入方式,在React Router客户端路由跳转场景下存在时序差:路由跳转触发Home组件挂载、useEffect内请求发起的时机,早于登录接口回调中defaults配置的生效时机,导致请求发出时读取到的还是Axios实例初始化时加载的旧令牌。手动刷新页面时,整个应用会重新初始化,Axios实例会重新读取localStorage里的新令牌,所以刷新后请求正常。
这种手动修改默认配置的方式本身也存在维护隐患,后续做令牌静默刷新时也很容易出现令牌不同步的问题。
修复方案
放弃手动修改Axios默认请求头的做法,改用Axios请求拦截器,每次请求发起前实时从localStorage读取最新令牌注入请求头,从根源上避免时序和不同步问题。
- 修改Axios实例定义文件,添加请求拦截器:
import axios from 'axios'; const axiosInstance = axios.create({ // 保留项目原有配置,比如baseURL、timeout等即可 baseURL: '/api', }); // 新增请求拦截器 axiosInstance.interceptors.request.use((config) => { const accessToken = localStorage.getItem('access_token'); if (accessToken) { config.headers.Authorization = `JWT ${accessToken}`; } return config; }); export default axiosInstance;
- 删除login.tsx中手动更新Axios默认请求头的代码,修改后登录成功回调如下:
.then((res) => { localStorage.setItem('access_token', res.data.access) localStorage.setItem('refresh_token', res.data.refresh) // 删除原手动修改请求头的代码行 navigate('/', { replace: true }) })
修改完成后,所有通过该实例发出的请求,都会在发起瞬间读取localStorage中存储的最新令牌,不管是登录跳转还是后续令牌刷新,都不会再出现携带旧令牌的问题,无需手动刷新页面。
内容的提问来源于stack exchange,提问作者João Queiroz
相关产品推荐
相关产品推荐

