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

相关推荐
VT.馒头4 小时前
【力扣】2695. 包装数组
前端·javascript·算法·leetcode·职场和发展·typescript
css趣多多4 小时前
一个UI内置组件el-scrollbar
前端·javascript·vue.js
C澒5 小时前
前端整洁架构(Clean Architecture)实战解析:从理论到 Todo 项目落地
前端·架构·系统架构·前端框架
C澒5 小时前
Remesh 框架详解:基于 CQRS 的前端领域驱动设计方案
前端·架构·前端框架·状态模式
Charlie_lll5 小时前
学习Three.js–雪花
前端·three.js
onebyte8bits5 小时前
前端国际化(i18n)体系设计与工程化落地
前端·国际化·i18n·工程化
C澒5 小时前
前端分层架构实战:DDD 与 Clean Architecture 在大型业务系统中的落地路径与项目实践
前端·架构·系统架构·前端框架
BestSongC5 小时前
行人摔倒检测系统 - 前端文档(1)
前端·人工智能·目标检测
lbb 小魔仙5 小时前
【HarmonyOS实战】React Native 鸿蒙版实战:Calendar 日历组件完全指南
react native·react.js·harmonyos
0思必得06 小时前
[Web自动化] Selenium处理滚动条
前端·爬虫·python·selenium·自动化