-
该 hook 封装了一个通用的请求发送和状态管理机制,支持自动或手动触发请求,并且支持请求取消。
-
它结合了 React 的
useState、useEffect、useRef来处理请求的生命周期和状态。 -
支持根据传递的配置自动或手动发送请求,且具备错误处理和请求取消的功能。
import axios ,{AxiosRequestConfig} from 'axios'
import {useState, useRef, useCallback,useEffect} from 'react'
import { useNavigate } from 'react-router-dom';
import { message } from '../utils/message';
// <T> 表示这个方法接收外部传递过来的一个类型
/**
* 重点:
* 1.2、T 就变成了传递进来的ResponseType(相当于实参)
* T 相当于(行参)
*/
// 默认请求数据
const defaultRequestConfig = {
url: '/',
method: 'GET',
data:{},
params: {}
}
// 意思是调请求的时候可以带一个manual参数boolean
// manual:true 告诉useRequest 手动触发请求
function useRequest<T>(options: AxiosRequestConfig & {manual?: boolean} = defaultRequestConfig) {
const navigate = useNavigate();
// 发送请求相关数据定义
// 请求接收到的内容
// 1.3 data的类型定义为ResponseType | null
const [data, setData] = useState<T | null>(null); // data的值可以是null也可以是外部传递过来的T的类型值
// 请求错误变量
const [error, setError] = useState('')
// 请求结束变量
const [loaded, setLoaded] = useState(false)
const controllerRef = useRef(new AbortController()) // 解除请求的api创建的实例// 它的改变不需要重新渲染页面
// 取消请求的方法(有定义请求的方法,就有取消请求的方法)
const cancel = ()=>{
controllerRef.current.abort()
}
const request = useCallback((requestOptions: AxiosRequestConfig)=>{ // requestOptions可传可不穿,传就用内部的,不传就用外部传过来的
// 每次请求前把请求状态和数据清空一下
setData(null);
setError('');
setLoaded(false)
const loginToken = localStorage.getItem('token');
const headers = loginToken ? {token: loginToken} : {};
// 发送请求
// useRequest把<T>向下传递到axios.request<T>
// 这样axios.request就会知道你返回的值,对应的data里的内容
// 就是<T>类型对应的内容
return axios.request<T>({
baseURL: 'http://statics.dell-lee.com/shopping-project/mock/',// 正常访问路径
url: requestOptions?.url, // 本地访问路径
method: requestOptions?.method,
signal: controllerRef.current.signal,
data: requestOptions?.data , // 请求的内容有哪些
params: requestOptions?.params,
headers // 发请求时携带登陆token给后端,有时候后端会希望我们把请求登陆的token塞到header里面去,获取方法见32行
/**
* 这样如果未来用useRequest发请求的时候,如果已经登陆过的话,
* 你登陆的token就会被带进请求的header中,方便后端去做鉴权
* 但是,token不是永远都有效的,它有有效期,如果你发token的时候,它正好失效了,
* 一般来说这种情况,虽然带了token给后端,但是后端会判断token是否过期,如果过期他们会给你做清空,
* 让你做重新登陆,这种后端会抛一个403的错,表示没有权限,前端要做相应的处理,见55行。
*/
}).then(response => {
// const a = response.data; // axios.request发送完请求后,它返回的response.data的类型就是上面传过来的<T>的类型
setData(response.data)
return response.data;
}).catch((e: any)=>{
if(e?.response?.status === 403){
localStorage.removeItem('token') // 去除之前过期的token,然后做页面跳转
navigate('/login');
}
setError(e.message || 'unknown request error')
throw new Error(e)
}).finally(()=>{
setLoaded(true);
})
}, [navigate]);
useEffect(()=>{
if(!options.manual){ //希望自动的时候才发请求
request(options).catch((e)=>{
message(e?.message)
})
}
}, [options, request])
// 1.4 把data返回,返回data的类型一定为ResponseType | null
// console.log('[data]',data)
return {data, error, loaded, request, cancel} // 因为这里把data return出去了,那么这个data 的类型是 Login index.tsx第十行useRequest<ResponseType>中的ResponseType 的类型
}
export default useRequest;
1. defaultRequestConfig 默认请求配置
js
复制编辑
const defaultRequestConfig = {
url: '/',
method: 'GET',
data: {},
params: {}
};
- 这是一个默认的请求配置对象,当
useRequest被调用时,如果没有传递自定义配置,会使用这个默认配置。 - 这里定义了
url、method、data和params,这些都是axios请求时常用的配置项。
2. useRequest Hook 定义
js
复制编辑
function useRequest<T>(options: AxiosRequestConfig & {manual?: boolean} = defaultRequestConfig)
useRequest是一个泛型函数,泛型T表示请求返回的数据类型。options是一个参数,类型是AxiosRequestConfig,也可以带一个额外的manual字段,表示是否手动触发请求。
3. useState 和 useRef 定义的状态
js
复制编辑
const [data, setData] = useState<T | null>(null);
const [error, setError] = useState('');
const [loaded, setLoaded] = useState(false);
const controllerRef = useRef(new AbortController());
data: 用来存储请求成功后的返回数据,类型是T | null,初始值为null。error: 存储请求错误信息,初始为空字符串。loaded: 标识请求是否完成,初始值为false。controllerRef: 用于保存AbortController实例,这个实例会用来取消请求。
4. cancel 函数
js
复制编辑
const cancel = ()=>{ controllerRef.current.abort(); }
cancel是一个取消请求的函数,调用时会触发AbortController的abort方法,从而取消正在进行中的请求。
5. request 函数
js
复制编辑
const request = useCallback((requestOptions: AxiosRequestConfig)=>{
// 请求状态和数据清空
setData(null);
setError('');
setLoaded(false);
const loginToken = localStorage.getItem('token');
const headers = loginToken ? {token: loginToken} : {};
return axios.request<T>({
baseURL: 'http://statics.dell-lee.com/shopping-project/mock/',
url: requestOptions?.url,
method: requestOptions?.method,
signal: controllerRef.current.signal,
data: requestOptions?.data,
params: requestOptions?.params,
headers
}).then(response => {
setData(response.data);
return response.data;
}).catch((e: any) => {
if(e?.response?.status === 403) {
localStorage.removeItem('token');
navigate('/login');
}
setError(e.message || 'unknown request error');
throw new Error(e);
}).finally(() => {
setLoaded(true);
});
}, [navigate]);
-
request函数发送axios请求并处理相关状态。- 在每次请求前,清空之前的数据、错误信息和请求状态。
- 从
localStorage中获取token,如果存在,则将其添加到请求头中,方便后端做鉴权。 - 使用
AbortController来支持取消请求。 - 请求成功后,将返回数据存入
data状态中。 - 请求失败时,若后端返回 403 错误(token 过期),清除本地
token并跳转到登录页面;否则设置错误信息。 - 请求完成后,设置
loaded为true。
6. useEffect 触发请求
js
复制编辑
useEffect(() => {
if (!options.manual) {
request(options).catch((e) => {
message(e?.message);
});
}
}, [options, request]);
- 在
useEffect中,判断manual是否为true。如果是false(即自动触发请求),则自动调用request函数。 - 如果请求失败,则调用
message函数显示错误信息。
7. 返回值
js
复制编辑
return { data, error, loaded, request, cancel };
-
useRequest返回一个对象,包含:data: 请求成功后的返回数据。error: 请求失败的错误信息。loaded: 请求是否完成的标识。request: 手动触发请求的函数。cancel: 取消请求的函数。