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

相关推荐
深蓝AI3 小时前
Meta 开源用了八年的设计系统 astryx:连坐两天 GitHub 总榜第一,组件库开始写给 agent 的说明书
前端·人工智能
IT_陈寒4 小时前
Vue的watch把我坑惨了,原来要这样用才靠谱
前端·人工智能·后端
1-1=04 小时前
Vue 3 模板语
前端·javascript·vue.js
用户7610304473714 小时前
【转战智能眼镜开发】ROKID AIUI 小游戏 亚马逊标枪项目实战讲解
前端
风卿4 小时前
看着AI写代码太孤独了,来欣赏星云吧:一个DSH 装饰插件
前端·javascript
牧艺4 小时前
弹层不用再手算坐标了:CSS Anchor Positioning 从原理到可上线
前端·javascript·css
用户老李4 小时前
YOLO 目标检测自学教程 07:YOLOv3——多尺度预测
前端
moshuying5 小时前
ESP32 墨水屏金价牌(上):从点亮到把图片推过去
前端
李剑一5 小时前
别再抬杠,前端岗已经没了!无论前后端,我们最终都要成“全干”工程师
前端·面试
Highcharts.js5 小时前
Highcharts 仪表盘与速度仪图表库介绍
前端·信息可视化·仪表板·highcharts·图表开发·图表库·仪表可视化