diff --git a/app/assets/material-library/page.tsx b/app/assets/material-library/page.tsx index 5685e86..92fcc73 100644 --- a/app/assets/material-library/page.tsx +++ b/app/assets/material-library/page.tsx @@ -133,6 +133,7 @@ export default function MaterialLibraryPage() { const fetchMaterialsRef = useRef(null) // 存储fetchMaterials函数引用 const materialsRequestIdRef = useRef(0) const materialsAbortRef = useRef(null) + const replacingMaterialsRequestIdRef = useRef(null) const facetsRequestIdRef = useRef(0) const facetsAbortRef = useRef(null) @@ -565,7 +566,12 @@ export default function MaterialLibraryPage() { // 获取素材列表(支持分页和追加模式) const fetchMaterials = useCallback( async (page = 1, append = false) => { + // 筛选刷新正在替换第一页时,禁止无限滚动请求抢占同一个请求通道。 + // 否则追加请求会取消筛选请求,却不会负责关闭筛选遮罩,导致页面永久转圈。 + if (append && replacingMaterialsRequestIdRef.current !== null) return 0 + const requestId = ++materialsRequestIdRef.current + if (!append) replacingMaterialsRequestIdRef.current = requestId materialsAbortRef.current?.abort() const controller = new AbortController() materialsAbortRef.current = controller @@ -661,6 +667,7 @@ export default function MaterialLibraryPage() { } } finally { if (!append && requestId === materialsRequestIdRef.current) { + replacingMaterialsRequestIdRef.current = null setLoading(false) setIsTransitionLoading(false) } @@ -993,7 +1000,7 @@ export default function MaterialLibraryPage() { // 分页+懒加载函数(完全参考flnxi_sucai实现) const handleLoadMore = useCallback(async () => { - if (isLoadingMore) return + if (isLoadingMore || isTransitionLoading || replacingMaterialsRequestIdRef.current !== null) return // 如果当前显示的数量小于已加载的素材数量,直接增加显示数量 if (displayedCount < materials.length) { @@ -1013,7 +1020,7 @@ export default function MaterialLibraryPage() { setDisplayedCount((prev) => Math.min(prev + 24, materials.length + appended)) } setIsLoadingMore(false) - }, [isLoadingMore, displayedCount, materials.length, hasMore, currentPage, fetchMaterials]) + }, [isLoadingMore, isTransitionLoading, displayedCount, materials.length, hasMore, currentPage, fetchMaterials]) // IntersectionObserver 逻辑已移至 LoadMoreTrigger 组件 @@ -1537,6 +1544,7 @@ export default function MaterialLibraryPage() { []) + root = null + rootMargin = '' + thresholds = [0] + + constructor(callback: ObserverCallback) { + this.callback = callback + IntersectionObserverMock.instances.push(this) + } + + trigger(isIntersecting: boolean) { + this.callback([{ isIntersecting } as IntersectionObserverEntry], this as unknown as IntersectionObserver) + } +} + +describe('LoadMoreTrigger', () => { + beforeEach(() => { + IntersectionObserverMock.instances = [] + window.IntersectionObserver = IntersectionObserverMock as unknown as typeof IntersectionObserver + }) + + test('筛选刷新期间不启动懒加载,刷新结束后恢复观察', () => { + const onLoadMore = jest.fn() + const { rerender } = render( + , + ) + + expect(IntersectionObserverMock.instances).toHaveLength(0) + + rerender( + , + ) + + expect(IntersectionObserverMock.instances).toHaveLength(1) + act(() => IntersectionObserverMock.instances[0].trigger(true)) + expect(onLoadMore).toHaveBeenCalledTimes(1) + }) + + test('筛选刷新开始时断开已有的懒加载观察器', () => { + const onLoadMore = jest.fn() + const { rerender } = render( + , + ) + const observer = IntersectionObserverMock.instances[0] + + rerender( + , + ) + + expect(observer.disconnect).toHaveBeenCalledTimes(1) + }) +}) diff --git a/components/home/LoadMoreTrigger.tsx b/components/home/LoadMoreTrigger.tsx index 2ea0bb9..9df1184 100644 --- a/components/home/LoadMoreTrigger.tsx +++ b/components/home/LoadMoreTrigger.tsx @@ -5,6 +5,7 @@ import React, { useEffect, useRef, forwardRef } from 'react' interface LoadMoreTriggerProps { hasMore: boolean isLoading: boolean + disabled?: boolean onLoadMore: () => void themeColor?: string displayedCount?: number // 当前显示数量 @@ -19,6 +20,7 @@ interface LoadMoreTriggerProps { const LoadMoreTrigger = forwardRef(function LoadMoreTrigger({ hasMore, isLoading, + disabled = false, onLoadMore, themeColor = '#1f2937', displayedCount, @@ -34,6 +36,7 @@ const LoadMoreTrigger = forwardRef(functio useEffect(() => { const element = typeof loadMoreRef === 'function' ? null : loadMoreRef.current if (!element) return + if (disabled) return if (isLoading) return if (!shouldShowLoadMore) return @@ -49,7 +52,7 @@ const LoadMoreTrigger = forwardRef(functio observer.observe(element) return () => observer.disconnect() - }, [loadMoreRef, hasMore, isLoading, onLoadMore, shouldShowLoadMore]) + }, [loadMoreRef, hasMore, isLoading, disabled, onLoadMore, shouldShowLoadMore]) // 显示加载更多 if (shouldShowLoadMore) {