265 lines
7.0 KiB
TypeScript
265 lines
7.0 KiB
TypeScript
import { useCallback, useRef, useState, useEffect } from 'react';
|
||
import { requestDeduplicator, requestCancelManager } from '../api/utils';
|
||
|
||
// 节流请求Hook
|
||
export const useThrottledRequest = <T extends (...args: any[]) => any>(
|
||
requestFn: T,
|
||
delay: number = 1000
|
||
) => {
|
||
const lastCallRef = useRef(0);
|
||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||
|
||
const throttledRequest = useCallback(
|
||
((...args: any[]) => {
|
||
const now = Date.now();
|
||
|
||
if (now - lastCallRef.current < delay) {
|
||
// 如果在节流时间内,取消之前的定时器并设置新的
|
||
if (timeoutRef.current) {
|
||
clearTimeout(timeoutRef.current);
|
||
}
|
||
|
||
timeoutRef.current = setTimeout(() => {
|
||
lastCallRef.current = now;
|
||
requestFn(...args);
|
||
}, delay - (now - lastCallRef.current));
|
||
} else {
|
||
// 如果超过节流时间,直接执行
|
||
lastCallRef.current = now;
|
||
requestFn(...args);
|
||
}
|
||
}) as T,
|
||
[requestFn, delay]
|
||
);
|
||
|
||
return throttledRequest;
|
||
};
|
||
|
||
// 防抖请求Hook
|
||
export const useDebouncedRequest = <T extends (...args: any[]) => any>(
|
||
requestFn: T,
|
||
delay: number = 300
|
||
) => {
|
||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||
|
||
const debouncedRequest = useCallback(
|
||
((...args: any[]) => {
|
||
if (timeoutRef.current) {
|
||
clearTimeout(timeoutRef.current);
|
||
}
|
||
|
||
timeoutRef.current = setTimeout(() => {
|
||
requestFn(...args);
|
||
}, delay);
|
||
}) as T,
|
||
[requestFn, delay]
|
||
);
|
||
|
||
return debouncedRequest;
|
||
};
|
||
|
||
// 带加载状态的请求Hook
|
||
export const useRequestWithLoading = <T extends (...args: any[]) => Promise<any>>(
|
||
requestFn: T
|
||
) => {
|
||
const [loading, setLoading] = useState(false);
|
||
|
||
const requestWithLoading = useCallback(
|
||
(async (...args: any[]) => {
|
||
if (loading) {
|
||
console.log('请求正在进行中,跳过重复请求');
|
||
return;
|
||
}
|
||
|
||
setLoading(true);
|
||
try {
|
||
const result = await requestFn(...args);
|
||
return result;
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}) as T,
|
||
[requestFn, loading]
|
||
);
|
||
|
||
return { requestWithLoading, loading };
|
||
};
|
||
|
||
// 组合Hook:节流 + 加载状态
|
||
export const useThrottledRequestWithLoading = <T extends (...args: any[]) => Promise<any>>(
|
||
requestFn: T,
|
||
delay: number = 1000
|
||
) => {
|
||
const { requestWithLoading, loading } = useRequestWithLoading(requestFn);
|
||
const throttledRequest = useThrottledRequest(requestWithLoading, delay);
|
||
|
||
return { throttledRequest, loading };
|
||
};
|
||
|
||
// 带错误处理的请求Hook
|
||
export const useRequestWithError = <T extends (...args: any[]) => Promise<any>>(
|
||
requestFn: T
|
||
) => {
|
||
const [loading, setLoading] = useState(false);
|
||
const [error, setError] = useState<string | null>(null);
|
||
|
||
const requestWithError = useCallback(
|
||
(async (...args: any[]) => {
|
||
setError(null);
|
||
setLoading(true);
|
||
|
||
try {
|
||
const result = await requestFn(...args);
|
||
return result;
|
||
} catch (err) {
|
||
const errorMessage = err instanceof Error ? err.message : '请求失败';
|
||
setError(errorMessage);
|
||
throw err;
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}) as T,
|
||
[requestFn]
|
||
);
|
||
|
||
return { requestWithError, loading, error, clearError: () => setError(null) };
|
||
};
|
||
|
||
// 带重试的请求Hook
|
||
export const useRequestWithRetry = <T extends (...args: any[]) => Promise<any>>(
|
||
requestFn: T,
|
||
maxRetries: number = 3,
|
||
retryDelay: number = 1000
|
||
) => {
|
||
const [loading, setLoading] = useState(false);
|
||
const [retryCount, setRetryCount] = useState(0);
|
||
|
||
const requestWithRetry = useCallback(
|
||
(async (...args: any[]) => {
|
||
setLoading(true);
|
||
setRetryCount(0);
|
||
|
||
let lastError: any;
|
||
|
||
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
||
try {
|
||
setRetryCount(attempt);
|
||
const result = await requestFn(...args);
|
||
return result;
|
||
} catch (error) {
|
||
lastError = error;
|
||
|
||
if (attempt === maxRetries) {
|
||
throw error;
|
||
}
|
||
|
||
// 等待后重试
|
||
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||
}
|
||
}
|
||
|
||
throw lastError;
|
||
}) as T,
|
||
[requestFn, maxRetries, retryDelay]
|
||
);
|
||
|
||
return { requestWithRetry, loading, retryCount };
|
||
};
|
||
|
||
// 可取消的请求Hook
|
||
export const useCancellableRequest = <T extends (...args: any[]) => Promise<any>>(
|
||
requestFn: T
|
||
) => {
|
||
const [loading, setLoading] = useState(false);
|
||
const abortControllerRef = useRef<AbortController | null>(null);
|
||
|
||
const cancellableRequest = useCallback(
|
||
(async (...args: any[]) => {
|
||
// 取消之前的请求
|
||
if (abortControllerRef.current) {
|
||
abortControllerRef.current.abort();
|
||
}
|
||
|
||
// 创建新的AbortController
|
||
abortControllerRef.current = new AbortController();
|
||
|
||
setLoading(true);
|
||
|
||
try {
|
||
const result = await requestFn(...args);
|
||
return result;
|
||
} finally {
|
||
setLoading(false);
|
||
abortControllerRef.current = null;
|
||
}
|
||
}) as T,
|
||
[requestFn]
|
||
);
|
||
|
||
const cancelRequest = useCallback(() => {
|
||
if (abortControllerRef.current) {
|
||
abortControllerRef.current.abort();
|
||
setLoading(false);
|
||
abortControllerRef.current = null;
|
||
}
|
||
}, []);
|
||
|
||
// 组件卸载时取消请求
|
||
useEffect(() => {
|
||
return () => {
|
||
if (abortControllerRef.current) {
|
||
abortControllerRef.current.abort();
|
||
}
|
||
};
|
||
}, []);
|
||
|
||
return { cancellableRequest, loading, cancelRequest };
|
||
};
|
||
|
||
// 组合Hook:节流 + 加载状态 + 错误处理
|
||
export const useThrottledRequestWithError = <T extends (...args: any[]) => Promise<any>>(
|
||
requestFn: T,
|
||
delay: number = 1000
|
||
) => {
|
||
const { requestWithError, loading, error, clearError } = useRequestWithError(requestFn);
|
||
const throttledRequest = useThrottledRequest(requestWithError, delay);
|
||
|
||
return { throttledRequest, loading, error, clearError };
|
||
};
|
||
|
||
// 组合Hook:防抖 + 加载状态 + 错误处理
|
||
export const useDebouncedRequestWithError = <T extends (...args: any[]) => Promise<any>>(
|
||
requestFn: T,
|
||
delay: number = 300
|
||
) => {
|
||
const { requestWithError, loading, error, clearError } = useRequestWithError(requestFn);
|
||
const debouncedRequest = useDebouncedRequest(requestWithError, delay);
|
||
|
||
return { debouncedRequest, loading, error, clearError };
|
||
};
|
||
|
||
// 请求状态监控Hook
|
||
export const useRequestMonitor = () => {
|
||
const [pendingCount, setPendingCount] = useState(0);
|
||
|
||
useEffect(() => {
|
||
const updatePendingCount = () => {
|
||
setPendingCount(requestDeduplicator.getPendingCount());
|
||
};
|
||
|
||
// 初始更新
|
||
updatePendingCount();
|
||
|
||
// 定期检查待处理请求数量
|
||
const interval = setInterval(updatePendingCount, 100);
|
||
|
||
return () => clearInterval(interval);
|
||
}, []);
|
||
|
||
const cancelAllRequests = useCallback(() => {
|
||
requestCancelManager.cancelAllRequests();
|
||
requestDeduplicator.clear();
|
||
}, []);
|
||
|
||
return { pendingCount, cancelAllRequests };
|
||
};
|