From f65f14e0efad26944cb1fadee433ab110859779c Mon Sep 17 00:00:00 2001 From: Ernst Kaese Date: Wed, 26 Nov 2025 11:04:50 +0100 Subject: [PATCH] fix: Prevent use-table-interaction-metrics from calling debounced function after unmounting --- .../use-table-interaction-metrics.test.tsx | 17 +++++++++++++++++ .../use-table-interaction-metrics/index.ts | 11 +++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/internal/hooks/use-table-interaction-metrics/__tests__/use-table-interaction-metrics.test.tsx b/src/internal/hooks/use-table-interaction-metrics/__tests__/use-table-interaction-metrics.test.tsx index 8db6c0b5dd..e0800beec7 100644 --- a/src/internal/hooks/use-table-interaction-metrics/__tests__/use-table-interaction-metrics.test.tsx +++ b/src/internal/hooks/use-table-interaction-metrics/__tests__/use-table-interaction-metrics.test.tsx @@ -273,5 +273,22 @@ describe('useTableInteractionMetrics', () => { }) ); }); + + test('componentUpdated should not be called after unmount', () => { + const { setLastUserAction, rerender, unmount } = renderUseTableInteractionMetricsHook({}); + + setLastUserAction('filter'); + rerender({ loading: true }); + rerender({ loading: false }); + + // Unmount before the debounced callback fires + unmount(); + + // Run timers to trigger the debounced callback + jest.runAllTimers(); + + // componentUpdated should not have been called because component was unmounted + expect(ComponentMetrics.componentUpdated).toHaveBeenCalledTimes(0); + }); }); }); diff --git a/src/internal/hooks/use-table-interaction-metrics/index.ts b/src/internal/hooks/use-table-interaction-metrics/index.ts index 072e089d95..ed6c7f8e31 100644 --- a/src/internal/hooks/use-table-interaction-metrics/index.ts +++ b/src/internal/hooks/use-table-interaction-metrics/index.ts @@ -49,10 +49,18 @@ export function useTableInteractionMetrics({ const lastUserAction = useRef<{ name: string; time: number } | null>(null); const capturedUserAction = useRef(null); const loadingStartTime = useRef(null); + const isMountedRef = useRef(true); const metadata = useRef({ itemCount, getComponentIdentifier, getComponentConfiguration, interactionMetadata }); metadata.current = { itemCount, getComponentIdentifier, getComponentConfiguration, interactionMetadata }; + useEffect(() => { + isMountedRef.current = true; + return () => { + isMountedRef.current = false; + }; + }, []); + useEffect(() => { if (isInFunnel) { return; @@ -94,6 +102,9 @@ export function useTableInteractionMetrics({ }, [instanceIdentifier, loading, taskInteractionId, isInFunnel]); const debouncedUpdated = useDebounceCallback(() => { + if (!isMountedRef.current) { + return; + } ComponentMetrics.componentUpdated({ taskInteractionId, componentName: 'table',