You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 06:30:50