React-query查询函数在上下文Token更新后未更新,如何解决?
解决React Query在Token刷新后仍使用旧Token的问题
问题描述
我通过useContext钩子在查询函数中传递作为请求头Bearer令牌的access token,但当access token一段时间后刷新时,React Query仍在查询函数中使用旧的access token。相关代码如下:
使用React Query的组件代码
const Profile = () => { const { fetchUser} = useAppContext(); const { data, isLoading, isError, error } = useQuery( ['users'], fetchUser ); //... }
上下文传递的Axios拦截器代码
const authFetch = axios.create({ baseURL: '/api/v1', headers: { Authorization: `Bearer ${state.accessToken}`, }, }); authFetch.interceptors.response.use( (response) => response, async (error) => { const status = error?.response?.status; if (status === 401) { const config = error.config; const accessToken = await refreshAccessToken(); config.headers['Authorization'] = `Bearer ${accessToken}`; return axios.request(config); } return Promise.reject(error); } );
await refreshAccessToken()从服务器获取新的access token并更新到上下文状态,然后返回该令牌。
上下文传递的查询函数代码
const fetchUser = async () => { const { data } = await authFetch.get('/users'); return data; };
解决方案
1. 动态添加请求头,避免硬编码Token
问题根源在于axios.create时设置的Authorization头是静态的,实例创建后不会自动同步上下文里的最新Token。改成用请求拦截器每次请求时动态获取最新Token:
const authFetch = axios.create({ baseURL: '/api/v1', // 移除静态的Authorization头 }); // 添加请求拦截器,每次请求时注入最新Token authFetch.interceptors.request.use( (config) => { const accessToken = state.accessToken; if (accessToken) { config.headers['Authorization'] = `Bearer ${accessToken}`; } return config; }, (error) => Promise.reject(error) ); // 保留响应拦截器的401刷新逻辑,注意用authFetch重发请求 authFetch.interceptors.response.use( (response) => response, async (error) => { const status = error?.response?.status; if (status === 401) { const config = error.config; const accessToken = await refreshAccessToken(); config.headers['Authorization'] = `Bearer ${accessToken}`; return authFetch.request(config); // 用authFetch而非axios,确保走拦截器 } return Promise.reject(error); } );
2. 让React Query感知Token变化,自动更新数据
Token刷新后,即使请求会用新Token,React Query的缓存数据还是旧的,需要让查询关联Token状态:
方法一:将Token加入查询键
把accessToken作为查询键的一部分,Token变化时React Query会自动触发重新查询:
const Profile = () => { const { fetchUser, accessToken } = useAppContext(); const { data, isLoading, isError, error } = useQuery( ['users', accessToken], // Token作为查询键的一部分 fetchUser ); //... };
方法二:监听Token变化手动触发重查
通过useEffect监听accessToken变化,调用refetch更新数据:
const Profile = () => { const { fetchUser, accessToken } = useAppContext(); const { data, isLoading, isError, error, refetch } = useQuery( ['users'], fetchUser ); useEffect(() => { refetch(); }, [accessToken, refetch]); //... };
3. 避免查询函数的闭包陷阱
确保fetchUser每次执行时能拿到最新的Token上下文。由于我们已经在请求拦截器中直接访问state.accessToken,每次请求都会获取最新值,不会因为闭包捕获旧状态而出问题。
内容的提问来源于stack exchange,提问作者Shikhar Mishra
相关产品推荐
相关产品推荐

