如何优雅地在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的直觉,又能够达到配置化的需求,优雅但功能依旧。

相关推荐
四岁半儿1 小时前
常用css
前端·css
你的人类朋友2 小时前
说说git的变基
前端·git·后端
姑苏洛言2 小时前
网页作品惊艳亮相!这个浪浪山小妖怪网站太治愈了!
前端
字节逆旅2 小时前
nvm 安装pnpm的异常解决
前端·npm
Jerry2 小时前
Compose 从 View 系统迁移
前端
GIS之路2 小时前
2025年 两院院士 增选有效候选人名单公布
前端
四岁半儿2 小时前
vue,H5车牌弹框定制键盘包括新能源车牌
前端·vue.js
烛阴2 小时前
告别繁琐的类型注解:TypeScript 类型推断完全指南
前端·javascript·typescript
gnip3 小时前
工程项目中.env 文件原理
前端·javascript
JefferyXZF3 小时前
Next.js Server Actions 详解: 无缝衔接前后端的革命性技术(八)
前端·全栈·next.js