React中如何在useEffect完成Axios拦截器配置后返回children?
解决Axios拦截器包装组件先渲染子组件导致JWT令牌过期问题
问题分析
核心矛盾在于:useEffect 是组件挂载完成后才执行的钩子,第一次渲染时子组件会先触发API请求,此时拦截器还未初始化,只能使用旧的JWT令牌,最终触发过期报错。你尝试用状态控制渲染时机,但TypeScript报错是因为组件返回值类型不匹配——false 不属于合法的React节点类型。
解决方案
1. 修复TypeScript类型错误
把返回逻辑从 return isSet && children 修改为条件渲染合法的React节点,比如返回加载占位组件或null:
const AxiosPrivateInterceptor: FC<IProps> = ({ children }) => { const { user } = useAppSelector(selectAuthUser); const refresh = useRefreshToken(); const [isSet, setIsSet] = useState<boolean>(false); useEffect(() => { const requestIntercept = axiosPrivate.interceptors.request.use( (config: AxiosRequestConfig) => { config.headers = config.headers ?? {}; if (!config?.headers["Authorization"]) { config.headers["Authorization"] = `Bearer ${user?.accessToken}`; } return config; }, (error) => Promise.reject(error) ); const responseIntercept = axiosPrivate.interceptors.response.use( (response) => response, async (error) => { const prevRequest = error?.config; if (error?.response.status === 403 && !prevRequest?.sent) { prevRequest.sent = true; // 修正拼写错误,标记请求已处理避免重复刷新 const newToken = await refresh(); const accessToken = newToken.accessToken; prevRequest.headers["Authorization"] = `Bearer ${accessToken}`; return axiosPrivate(prevRequest); } return Promise.reject(error); } ); setIsSet(true); return () => { axiosPrivate.interceptors.request.eject(requestIntercept); axiosPrivate.interceptors.response.eject(responseIntercept); }; }, [user, refresh]); // 返回合法React节点,TypeScript不再报错 return isSet ? children : <div>加载中...</div>; // 无需加载提示可直接返回null // return isSet ? children : null; };
2. 确保user加载完成后再初始化拦截器
如果初始时user可能处于未加载状态(比如Redux还在异步获取用户数据),可以在useEffect中先判断user是否存在,再初始化拦截器:
useEffect(() => { // 等待user就绪后再设置拦截器 if (!user) return; const requestIntercept = axiosPrivate.interceptors.request.use(/* 原有逻辑 */); const responseIntercept = axiosPrivate.interceptors.response.use(/* 原有逻辑 */); setIsSet(true); return () => { axiosPrivate.interceptors.request.eject(requestIntercept); axiosPrivate.interceptors.response.eject(responseIntercept); }; }, [user, refresh]);
这能避免user未就绪时设置拦截器,导致accessToken为空的问题。
3. 可选:拦截器直接读取最新user状态
如果担心useEffect依赖更新不及时,可以在请求拦截器中直接从Redux store获取最新的user,无需依赖useEffect的user参数:
const requestIntercept = axiosPrivate.interceptors.request.use( (config: AxiosRequestConfig) => { config.headers = config.headers ?? {}; if (!config?.headers["Authorization"]) { // 直接从store实例获取最新用户数据 const currentUser = store.getState().auth.user; config.headers["Authorization"] = `Bearer ${currentUser?.accessToken}`; } return config; }, (error) => Promise.reject(error) );
这种方式能确保每次请求都拿到最新令牌,但需要你能直接访问Redux store实例。
内容的提问来源于stack exchange,提问作者Mohammad Rafi
相关产品推荐
相关产品推荐

