From 2661d65ab8a8a20806f2c708cd167fa5779c22b2 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 01:45:05 +0000 Subject: [PATCH] Refactor: Debounce effect fetches --- .../moderation/useModerationQueueManager.ts | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/hooks/moderation/useModerationQueueManager.ts b/src/hooks/moderation/useModerationQueueManager.ts index 402b638d..129b4ce0 100644 --- a/src/hooks/moderation/useModerationQueueManager.ts +++ b/src/hooks/moderation/useModerationQueueManager.ts @@ -128,6 +128,8 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig): const fetchItemsRef = useRef<((silent?: boolean) => Promise) | null>(null); const FETCH_COOLDOWN_MS = 1000; + const EFFECT_DEBOUNCE_MS = 50; // Short debounce to let all effects settle + const effectFetchTimerRef = useRef(null); // Store settings in refs to avoid re-creating fetchItems const settingsRef = useRef(settings); @@ -856,19 +858,26 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig): // eslint-disable-next-line react-hooks/exhaustive-deps }, [user?.id]); + // Debounced fetch for effects - prevents race conditions + const debouncedEffectFetch = useCallback(() => { + if (effectFetchTimerRef.current) { + clearTimeout(effectFetchTimerRef.current); + } + + effectFetchTimerRef.current = setTimeout(() => { + console.log('[Debounced Fetch] Executing after effects settled'); + fetchItemsRef.current?.(true); + }, EFFECT_DEBOUNCE_MS); + }, []); + // Filter and tab changes trigger refetch useEffect(() => { if (!user || !initialFetchCompleteRef.current || isMountingRef.current) return; - console.log('[Filter/Tab Change] Refetching with:', { - tab: filters.activeTab, - entity: filters.debouncedEntityFilter, - status: filters.debouncedStatusFilter - }); - + console.log('[Filter/Tab Change] Queuing debounced fetch'); pagination.reset(); - fetchItemsRef.current?.(true); - }, [filters.activeTab, filters.debouncedEntityFilter, filters.debouncedStatusFilter, user]); + debouncedEffectFetch(); + }, [filters.activeTab, filters.debouncedEntityFilter, filters.debouncedStatusFilter, user, debouncedEffectFetch, pagination]); // Sort changes trigger refetch useEffect(() => { @@ -876,20 +885,26 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig): return; } - console.log('[Sort Change] Refetching with:', { - field: sort.field, - direction: sort.direction - }); + console.log('[Sort Change] Queuing debounced fetch'); pagination.reset(); - fetchItemsRef.current?.(true); - }, [sort.field, sort.direction, user, pagination]); + debouncedEffectFetch(); + }, [sort.field, sort.direction, user, pagination, debouncedEffectFetch]); // Pagination changes trigger refetch useEffect(() => { if (!user || !initialFetchCompleteRef.current || pagination.currentPage === 1) return; - fetchItemsRef.current?.(true); - }, [pagination.currentPage, pagination.pageSize]); + debouncedEffectFetch(); + }, [pagination.currentPage, pagination.pageSize, debouncedEffectFetch]); + + // Cleanup effect timer on unmount + useEffect(() => { + return () => { + if (effectFetchTimerRef.current) { + clearTimeout(effectFetchTimerRef.current); + } + }; + }, []); // Polling effect (when realtime disabled) useEffect(() => {