From 5608458be6dca84ed64755dba797dcdcde9fe40d Mon Sep 17 00:00:00 2001 From: km2025 Date: Fri, 6 Feb 2026 14:31:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=20useTemplateGenerat?= =?UTF-8?q?ions=20=E9=92=A9=E5=AD=90=EF=BC=8C=E6=B7=BB=E5=8A=A0=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=8F=96=E6=B6=88=E9=80=BB=E8=BE=91=EF=BC=8C=E6=94=B9?= =?UTF-8?q?=E8=BF=9B=E6=95=B0=E6=8D=AE=E8=BF=87=E6=BB=A4=E5=92=8C=E5=88=86?= =?UTF-8?q?=E9=A1=B5=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hooks/data/use-template-generations.ts | 101 ++++++++++++++++--------- 1 file changed, 66 insertions(+), 35 deletions(-) diff --git a/hooks/data/use-template-generations.ts b/hooks/data/use-template-generations.ts index c903647..9f1d797 100644 --- a/hooks/data/use-template-generations.ts +++ b/hooks/data/use-template-generations.ts @@ -8,85 +8,108 @@ import { handleError } from './use-error' const pageSize = 12 +// 公共工具函数 +const getController = () => root.get(TemplateGenerationController) + +const filterValidItems = (items: unknown[]): TemplateGeneration[] => + (items || []).filter((item: unknown): item is TemplateGeneration => { + return Boolean( + item && typeof item === 'object' && item !== null && 'id' in item && (item as Record).id, + ) + }) + +const calculateHasMore = (items: unknown[], limit: number): boolean => Array.isArray(items) && items.length === limit + export const useTemplateGenerations = () => { const [loading, setLoading] = useState(true) const [loadingMore, setLoadingMore] = useState(false) const [error, setError] = useState(null) const [data, setData] = useState([]) + const [hasMore, setHasMore] = useState(true) const currentPageRef = useRef(1) - const hasMoreRef = useRef(true) + const abortControllerRef = useRef(null) const load = useCallback(async (params?: ListTemplateGenerationsInput) => { + // 取消之前的请求 + if (abortControllerRef.current) { + abortControllerRef.current.abort() + } + abortControllerRef.current = new AbortController() + setLoading(true) setError(null) currentPageRef.current = 1 - const templateGeneration = root.get(TemplateGenerationController) - const { data, error } = await handleError( - async () => - await templateGeneration.list({ - page: 1, - limit: params?.limit || pageSize, - ...params, - }), + const limit = params?.limit || pageSize + const { data: responseData, error } = await handleError(async () => + getController().list({ + page: 1, + limit, + ...params, + }), ) + // 如果请求被中止,直接返回 + if (abortControllerRef.current?.signal.aborted) { + return + } + if (error) { setError(error) setLoading(false) - return + return { data: undefined, error } } - const items = data?.data || [] - - hasMoreRef.current = items.length >= (params?.limit || pageSize) - - const filterData = items?.filter((item) => !!item.id) + const items = responseData?.data || [] + const filterData = filterValidItems(items) + const hasMoreResult = calculateHasMore(items, limit) setData(filterData) + setHasMore(hasMoreResult) setLoading(false) - return { data, error: null } + abortControllerRef.current = null + return { data: filterData, error: null } }, []) const loadMore = useCallback( async (params?: Omit) => { - const hasMore = hasMoreRef.current - if (!hasMore) return + // 防止竞态条件和无效请求 + if (loading || loadingMore || !hasMore) return setLoadingMore(true) const nextPage = currentPageRef.current + 1 + const limit = params?.limit || pageSize - const templateGeneration = root.get(TemplateGenerationController) - - const { data: newData, error } = await handleError( - async () => - await templateGeneration.list({ - page: nextPage, - limit: params?.limit || pageSize, - ...params, - }), + const { data: responseData, error } = await handleError(async () => + getController().list({ + page: nextPage, + limit, + ...params, + }), ) if (error) { setLoadingMore(false) + setError(error) return { data: undefined, error } } - const newItems = newData?.data || [] - hasMoreRef.current = newItems.length >= (params?.limit || pageSize) + const newItems = responseData?.data || [] + const filterData = filterValidItems(newItems) + const hasMoreResult = calculateHasMore(newItems, limit) currentPageRef.current = nextPage - const filterData = newItems?.filter((item) => !!item.id) setData((prev) => [...prev, ...filterData]) + setHasMore(hasMoreResult) setLoadingMore(false) - return { data: newData, error: null } + return { data: filterData, error: null } }, - [loading, loadingMore], + [loading, loadingMore, hasMore], ) const refetch = useCallback( (params?: ListTemplateGenerationsInput) => { - hasMoreRef.current = true + setHasMore(true) return load(params) }, [load], @@ -94,7 +117,15 @@ export const useTemplateGenerations = () => { useEffect(() => { load() - }, []) + + // 清理函数:组件卸载时取消未完成的请求 + return () => { + if (abortControllerRef.current) { + abortControllerRef.current.abort() + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) // 只在组件挂载时执行一次初始加载 return { data, @@ -104,6 +135,6 @@ export const useTemplateGenerations = () => { load, refetch, loadMore, - hasMore: hasMoreRef.current, + hasMore, } }