如何优雅地在react hook中使用Axios
在react的项目中有多种方式可以使用Axios
- 直接在组件内部调用Axios
- 将Axios进行封装之后,在组件内部进行调用
但是在封装这一层上面怎么样才能达到符合react hooks的方式呢,自然是将Axios封装成为hook是符合逻辑的。
useAxios
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
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 登录;
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的直觉,又能够达到配置化的需求,优雅但功能依旧。
转载自:https://juejin.cn/post/7276059583201624103