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

相关推荐
Mintopia1 分钟前
B 样条曲线:计算机图形学里的 “曲线魔术师”
前端·javascript·计算机图形学
前端小巷子4 分钟前
跨域问题解决方案:CORS(跨域资源共享)
前端·网络协议·面试
大大。5 分钟前
van-tabbar-item选中active数据变了,图标没变
java·服务器·前端
Mintopia7 分钟前
Three.js 3D 世界中的噪声运动:当数学与像素共舞
前端·javascript·three.js
nc_kai7 分钟前
Flutter 之 每日翻译 PreferredSizeWidget
java·前端·flutter
来碗疙瘩汤10 分钟前
使用 Three.js 与 CSS3DRenderer 在 Vue3 中加载网页为 3D 模型
前端·javascript
满分观察网友z11 分钟前
富文本解析终极指南:从Quill到小程序,我如何用正则摆平所有坑?
前端
打野赵怀真12 分钟前
在TypeScript中装饰器有哪些应用场景?
前端·javascript
destinying14 分钟前
vite学习笔记
前端·javascript
LRH15 分钟前
JS基础 - 手写数组扁平化函数
前端·javascript