react-封裝請求

210 阅读5分钟
  • 该 hook 封装了一个通用的请求发送和状态管理机制,支持自动或手动触发请求,并且支持请求取消。

  • 它结合了 React 的 useStateuseEffectuseRef 来处理请求的生命周期和状态。

  • 支持根据传递的配置自动或手动发送请求,且具备错误处理和请求取消的功能。

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 被调用时,如果没有传递自定义配置,会使用这个默认配置。
  • 这里定义了 urlmethoddataparams,这些都是 axios 请求时常用的配置项。

2. useRequest Hook 定义

js
复制编辑
function useRequest<T>(options: AxiosRequestConfig & {manual?: boolean} = defaultRequestConfig)
  • useRequest 是一个泛型函数,泛型 T 表示请求返回的数据类型。
  • options 是一个参数,类型是 AxiosRequestConfig,也可以带一个额外的 manual 字段,表示是否手动触发请求。

3. useStateuseRef 定义的状态

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 是一个取消请求的函数,调用时会触发 AbortControllerabort 方法,从而取消正在进行中的请求。

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 并跳转到登录页面;否则设置错误信息。
    • 请求完成后,设置 loadedtrue

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: 取消请求的函数。