useRequest
import { post } from '@/api/http';
import { IResponse } from '@/types/common';
import { isString } from '@/utils/is';
import { useCallback, useEffect, useRef, useState } from 'react';
import { debounce, throttle } from 'lodash-es';
import useDebounce from './useDebounce';
import useThrottle from './useThrottle';
type TApiFn<P = any, T = any> = (params: P) => Promise<IResponse<T>>;
type TApi = string | TApiFn;
const enum EType {
debounce = 'debounce',
throttle = 'throttle',
}
interface IOptions<P = any, T = any> {
manual?: boolean;
ready?: boolean;
params?: P;
type?: `${EType}`;
delay?: number;
pollInterval?: number;
onSuccess?: (data: T) => void;
onFail?: () => void;
}
interface IUseRequestResult<P = any, T = any> {
data: T;
loading: boolean;
request: (params?: P) => void;
stopPolling: () => void;
}
const useRequest = <P = any, T = any>(api: TApi, options: IOptions): IUseRequestResult<P, T> => {
const { manual, ready, params, pollInterval, type, delay } = options;
const pollRef = useRef<number>();
const pollingRef = useRef<boolean>(false);
const [loading, setLoading] = useState<boolean>(false);
const [data, setData] = useState<T>();
const onRequest = useCallback(
(params?: P): Promise<T> => {
let request: unknown = api;
if (isString(api)) {
request = (params?: P) => post(api as string, params);
}
setLoading(true);
return (request as TApiFn<P, T>)(params)
.then((response: IResponse<T>) => {
const { success, data } = response;
if (success) {
setData(data);
return data;
} else {
throw response;
}
})
.finally(() => setLoading(false));
},
[api]
);
const stopPolling = () => {
if (pollRef.current) {
pollingRef.current = false;
clearInterval(pollRef.current);
}
};
const request = useCallback(
(_params: P = params) => {
if (pollingRef.current) return;
if (pollInterval) {
pollingRef.current = true;
pollRef.current = setInterval(() => onRequest(_params), pollInterval);
} else {
onRequest(_params);
}
},
[onRequest]
);
const debounceRequest = useDebounce(request, delay);
const throttleRequest = useThrottle(request, delay);
const readyRequest = useCallback(() => {
if (type === EType.debounce) {
return debounceRequest();
}
if (type === EType.throttle) {
return throttleRequest();
}
return request();
}, [request]);
useEffect(() => {
if (!manual && ready) {
readyRequest();
}
}, [api, manual, ready]);
return { loading, data, request, stopPolling };
};
export default useRequest;
useDebounce
import { useEffect, useRef } from 'react';
import { debounce } from 'lodash-es';
const useDebounce = (callback: (...args: any) => void, delay: number = 500) => {
const debounceRef = useRef<{ cancel: () => void } & ((...args: any) => void)>();
useEffect(() => {
debounceRef.current = debounce(callback, delay);
return () => {
debounceRef.current?.cancel();
};
}, [callback, delay]);
return debounceRef.current;
};
export default useDebounce;
useThrottle
import { useEffect, useRef } from 'react';
import { throttle } from 'lodash-es';
const useThrottle = (callback: (...args: any) => void, delay: number = 500) => {
const debounceRef = useRef<{ cancel: () => void } & ((...args: any) => void)>();
useEffect(() => {
debounceRef.current = throttle(callback, delay);
return () => {
debounceRef.current?.cancel();
};
}, [callback, delay]);
return debounceRef.current;
};
export default useThrottle;