Skip to content

fix(frontend): prevent memory leak in useIdempotentAction.ts (#1221) - #1293

Merged
github-actions[bot] merged 1 commit into
leojay-net:mainfrom
Mercy017:fix/issue-1221-idempotent-action-memory-leak
Jul 27, 2026
Merged

fix(frontend): prevent memory leak in useIdempotentAction.ts (#1221)#1293
github-actions[bot] merged 1 commit into
leojay-net:mainfrom
Mercy017:fix/issue-1221-idempotent-action-memory-leak

Conversation

@Mercy017

Copy link
Copy Markdown
Contributor

Root cause

useIdempotentAction's useEffect cleanup only set isMountedRef.current = false. The inFlightActions ref (Map<string, Promise<unknown>>) was never cleared on unmount.

Any Promise stored in that Map — together with the closures it captured (state setters, isProcessingRef, lastExecutionTime, etc.) — was retained in memory until the Promise settled. On high-churn routes where the hook's component mounts and unmounts frequently, these retained closures accumulate and prevent GC of the entire component subtree.

Fix (src/hooks/useIdempotentAction.ts)

Added inFlightActions.current.clear() to the useEffect cleanup so in-flight Promise references are released the moment the component unmounts, allowing the GC to reclaim them.

  return () => {
    isMountedRef.current = false;
+   inFlightActions.current.clear();
  };

Regression test (src/hooks/__tests__/useIdempotentAction.test.ts)

New test "regression: clears inFlightActions on unmount …":

  1. Starts an action, leaving a Promise in inFlightActions.
  2. Unmounts the component while the Promise is in flight.
  3. Resolves the Promise after unmount — verifies no stale-state update throws.
  4. Confirms a fresh hook instance executes normally, proving no lingering state.

Closes #1221
Closes #1220

…IdempotentAction.ts (leojay-net#1221)

Root cause: the useEffect cleanup only set isMountedRef.current = false.
The inFlightActions Map (Map<string, Promise<unknown>>) was never cleared,
so every Promise stored in it — together with the closures they captured
(state setters, refs, hook internals) — was retained in memory until the
Promise settled, even after the component unmounted.

Fix: call inFlightActions.current.clear() in the effect cleanup so
in-flight Promise references are released on unmount.

Regression test added: starts an action, unmounts while the Promise is
still pending, then resolves it — verifies no stale-state update occurs
and that a fresh hook instance behaves normally.
@drips-wave

drips-wave Bot commented Jul 27, 2026

Copy link
Copy Markdown

@Mercy017 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@github-actions
github-actions Bot merged commit 598a802 into leojay-net:main Jul 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants