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

如何在Axios拦截器中访问React useContext上下文方法

可行实现方案

React的useContext钩子仅能在React函数组件、自定义Hook的顶层作用域调用,普通JS模块无法直接通过该方式获取上下文值,以下是两种可落地的无侵入改造方案。


方案1:Axios实例增加上下文注入入口(优先推荐)

该方案不需要改动现有组件的嵌套结构,仅需给Axios实例暴露一个注入通知方法的入口,在Provider初始化时传入上下文方法即可。

  1. 改造apiClient.js
import axios from 'axios';

const apiClient = axios.create({
    baseURL: process.env.REACT_APP_API,
    headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
    },
    withCredentials: true,
    timeout: 10000,
});

// 初始化通知方法为空函数,避免注入前调用报错
let addNotification = () => {};

// 暴露注入方法,供React组件传入上下文内的通知方法
apiClient.injectNotification = (notifyMethod) => {
    addNotification = notifyMethod;
};

// 请求拦截器
apiClient.interceptors.request.use(
    (config) => config,
    (error) => {
        addNotification('error', '请求发送失败', error.message || '请检查请求参数后重试');
        return Promise.reject(error);
    },
);

// 响应拦截器
apiClient.interceptors.response.use(
    (response) => Promise.resolve(response),
    (error) => {
        if (!error.response) {
            addNotification('error', '网络异常', '请检查设备网络连接后重试');
        } else {
            const errMsg = error.response.data?.message || `服务端返回错误,状态码:${error.response.status}`;
            addNotification('error', '请求失败', errMsg);
        }
        return Promise.reject(error);
    },
);

export default apiClient;
  1. 改造NotificationContext.js
    在Provider挂载时将上下文内的addNotification方法注入到Axios实例,同时修复原代码中状态更新的闭包陷阱:
import React, { createContext, useState, useEffect } from 'react';
import apiClient from '../utils/apiClient'; // 替换为实际的apiClient路径

const NotificationContext = createContext();

const NotificationProvider = ({ children }) => {
    const [notifications, setNotifications] = useState([]);

    const addNotification = (type, title, description) => {
        const notification = {
            id: Date.now(),
            type,
            title,
            description,
        };
        // 使用函数式更新,避免闭包导致的旧状态问题
        setNotifications(prev => [notification, ...prev]);
    };

    const removeNotification = (id) => {
        setNotifications(prev => prev.filter((notification) => notification.id !== id));
    };

    // 组件挂载时完成通知方法注入
    useEffect(() => {
        apiClient.injectNotification(addNotification);
    }, [addNotification]);

    return (
        <NotificationContext.Provider value={{ notifications, addNotification, removeNotification }}>
            {children}
        </NotificationContext.Provider>
    );
};

export { NotificationContext, NotificationProvider };

原代码中setNotifications直接依赖外部作用域的notifications变量,连续触发通知时会因为闭包拿到旧状态导致通知丢失,改为函数式更新可彻底解决该问题。


方案2:组件内注册拦截器

如果不想给Axios实例增加额外方法,可以将拦截器注册逻辑迁移到NotificationProvider组件内部,直接在组件作用域访问上下文方法,同时记得在组件卸载时移除拦截器避免内存泄漏。

  1. 简化apiClient.js,仅保留实例创建逻辑
import axios from 'axios';

const apiClient = axios.create({
    baseURL: process.env.REACT_APP_API,
    headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
    },
    withCredentials: true,
    timeout: 10000,
});

export default apiClient;
  1. 在NotificationContext.js内完成拦截器注册
import React, { createContext, useState, useEffect } from 'react';
import apiClient from '../utils/apiClient';

const NotificationContext = createContext();

const NotificationProvider = ({ children }) => {
    const [notifications, setNotifications] = useState([]);

    const addNotification = (type, title, description) => {
        const notification = {
            id: Date.now(),
            type,
            title,
            description,
        };
        setNotifications(prev => [notification, ...prev]);
    };

    const removeNotification = (id) => {
        setNotifications(prev => prev.filter((notification) => notification.id !== id));
    };

    useEffect(() => {
        // 注册请求拦截器
        const reqInterceptorId = apiClient.interceptors.request.use(
            (config) => config,
            (error) => {
                addNotification('error', '请求发送失败', error.message || '请检查请求参数后重试');
                return Promise.reject(error);
            }
        );

        // 注册响应拦截器
        const resInterceptorId = apiClient.interceptors.response.use(
            (response) => response,
            (error) => {
                if (!error.response) {
                    addNotification('error', '网络异常', '请检查设备网络连接后重试');
                } else {
                    const errMsg = error.response.data?.message || `服务端返回错误,状态码:${error.response.status}`;
                    addNotification('error', '请求失败', errMsg);
                }
                return Promise.reject(error);
            }
        );

        // 组件卸载时移除拦截器,避免内存泄漏
        return () => {
            apiClient.interceptors.request.eject(reqInterceptorId);
            apiClient.interceptors.response.eject(resInterceptorId);
        };
    }, [addNotification]);

    return (
        <NotificationContext.Provider value={{ notifications, addNotification, removeNotification }}>
            {children}
        </NotificationContext.Provider>
    );
};

export { NotificationContext, NotificationProvider };

避坑说明

  • 不要尝试直接在普通JS模块中导入NotificationContext后读取值,React Context的取值依赖组件树的Provider层级,脱离组件树无法获取当前上下文的value。
  • 不要用全局变量硬编码存储addNotification方法,会存在初始化时序问题,容易出现拦截器触发时方法还未赋值的报错。

内容的提问来源于stack exchange,提问作者Thore

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 20:27:15