如何优雅地在react hook中使用Axios

在react的项目中有多种方式可以使用Axios

  1. 直接在组件内部调用Axios
  2. 将Axios进行封装之后,在组件内部进行调用

但是在封装这一层上面怎么样才能达到符合react hooks的方式呢,自然是将Axios封装成为hook是符合逻辑的。

useAxios

js 复制代码
export default ({url, method, payload}) => {
    const [data, setData] = useState(null);
    const [error, setError] = useState('');
    const [loading, setLoading] = useState(false);
    const controllerRef = useRef(new AbortController());
    const cancel = () => {
        controllerRef.current.abort();
    };

    const sendRequest = async () => {
        try {
            setLoading(true);
            const response = await axios.request({
                signal: controllerRef.current.signal,
                data: payload,
                method,
                url,
            });

            setData(response.data);
        } catch (error) {
            setError(error.message);
        } finally {
            setLoading(false);
        }
    }

    return { cancel, data, error, loading, sendRequest };
};

// other.js
const {
    sendRequest,
    data,
    loading,
} = useAxios({
    method: 'get',
    url: 'url'
});

这个hook可以在组件内部使用,然后得到自己想要的数据,但是它有一个问题就是不能够全局定制config,如果我需要全局定制一个 baseUrl,或者添加一些 interceptors,这时候就需要用到react给我们提供的 context 来进行升级。

AxiosInstanceProvider

js 复制代码
import axios from "axios";
import { createContext, useRef, useEffect } from 'react';

export const AxiosContext = createContext(null);
const AxiosInstanceProvider = ({
  config = {},
  requestInterceptors = [],
  responseInterceptors = [],
  children,
}) => {
  const instanceRef = useRef(axios.create(config));

  useEffect(() => {
    requestInterceptors.forEach((interceptor) => {
      instanceRef.current.interceptors.request.use(
        interceptor
      );
    });
    responseInterceptors.forEach((interceptor) => {
      instanceRef.current.interceptors.response.use(
        interceptor
      );
    });
  }, []);

  return (
    <AxiosContext.Provider value={instanceRef.current}>
      {children}
    </AxiosContext.Provider>
  );
};

export default AxiosInstanceProvider;

// 在useAxios里面也需要做一下小改动
import { useContext, useMemo, useState,useRef } from 'react';
import axios from 'axios';
import { AxiosContext } from './index';

export default ({url, method, payload}) => {
    const [data, setData] = useState(null);
    const [error, setError] = useState('');
    const [loading, setLoading] = useState(false);
    const contextInstance = useContext(AxiosContext); // 从context中获取配置好的axios
    const instance = useMemo(() => {
        return contextInstance || axios;
    }, [contextInstance]);
    const controllerRef = useRef(new AbortController());
    const cancel = () => {
        controllerRef.current.abort();
    };

    const sendRequest = async () => {
        try {
            setLoading(true);
            const response = await instance.request({
                signal: controllerRef.current.signal,
                data: payload,
                method,
                url,
            });

            setData(response.data);
        } catch (error) {
            setError(error.message);
        } finally {
            setLoading(false);
        }
    }

    return { cancel, data, error, loading, sendRequest };
};

这样也能处理在我们使用某些数据的时候,它的来源是通过 hook 获得的,但是在以往写在纯函数的封装里面是不能够使用 hook 的,例如微软的 Oath 登录;

js 复制代码
const { accounts, instance } = useMsal();
const setAccessTokenInAxiosConfig = async (config) => {
    const request = {
        scopes: ["User.Read"],
        account: accounts[0]
    };
    try {
        const { accessToken } = await instance.acquireTokenSilent(request);
        config.headers = {
            ...config.headers,
            Authorization: `Bearer ${accessToken}`
        }
        return config;
    } catch (error) {
        if (error instanceof InteractionRequiredAuthError) {
            console.log(error);
        }
    }
}
return (
    <AxiosInstanceProvider
        requestInterceptors={[
            setAccessTokenInAxiosConfig
        ]}
    >
        <PageLayout>
            <center>
                <MainContent />
            </center>
        </PageLayout>
    </AxiosInstanceProvider>
);

通过 provider 组件,就能够将拦截器注册到当前系统的请求框架里面,这样子使用Axios即符合react hook的直觉,又能够达到配置化的需求,优雅但功能依旧。

相关推荐
虚焦像素_3 小时前
海康 Web 无插件版接入指南:用法与注意事项
前端
aaron4 小时前
前端演进史:当你没留意时,前端发生了什么
前端
不好听6134 小时前
前端路由完全指南(上篇):从原理到 6 种路由模式
前端·react.js
像我这样帅的人丶你还4 小时前
🚀大文件上传的那些事
前端·javascript·架构
不好听6134 小时前
前端路由完全指南(中篇):React Router Hooks 完全指南
前端·react.js
hunterandroid4 小时前
[鸿蒙从零到一] ArkUI 声明式 UI 基础实战
前端
七牛开发者4 小时前
RAG 工程里的上下文剪枝:如何减少 68% 的上下文
前端·css·github
橘子星4 小时前
从零理解流式输出 —— 一个 Vue + DeepSeek 的前端实战
前端·javascript·人工智能
智驭未来掌门人4 小时前
如何让你的前端服务在服务器上nohub运行
前端
颜进强4 小时前
Claude Code - 18 从 Figma 到 React:一套让 AI 生成代码"不瞎编"的实战工作流
前端·后端·ai编程