Skip to content

fix: prevent stale state updates in useStellarBalance after unmount#10

Open
mertcicekci0 wants to merge 1 commit intostellar:mainfrom
mertcicekci0:fix/use-stellar-balance-cleanup
Open

fix: prevent stale state updates in useStellarBalance after unmount#10
mertcicekci0 wants to merge 1 commit intostellar:mainfrom
mertcicekci0:fix/use-stellar-balance-cleanup

Conversation

@mertcicekci0
Copy link
Copy Markdown
Contributor

Summary

  • useStellarBalance hook fires async RPC calls (balance + decimals + metadata) in a useEffect but has no cleanup — if the component unmounts or address changes mid-flight, the
    resolved promises still call setState on a stale component
  • Added a cancelledRef that is set to true on effect cleanup, checked before every state update in both refreshBalance and fetchAssetMetadata
  • Standard React pattern for preventing state updates after unmount

Changes

File Change
packages/paywall/src/browser/useStellarBalance.ts Add useRef cancellation flag + effect cleanup

Test plan

  • pnpm --filter @x402-stellar/paywall typecheck passes
  • Connect/disconnect wallet rapidly — no React "setState on unmounted component" warnings
  • Balance still loads correctly on normal wallet connection flow

Add cancellation ref to skip state updates when the effect is cleaned
up (component unmount or dependency change). Without this, in-flight
RPC calls for balance/metadata continue resolving and call setState
on an unmounted component, causing a memory leak and React warnings.
Copilot AI review requested due to automatic review settings March 6, 2026 15:53
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a cancellation mechanism to the useStellarBalance React hook to prevent stale state updates when the component unmounts or the address prop changes while async RPC calls are in-flight. This follows the standard React pattern of using a ref-based cancellation flag tied to effect cleanup.

Changes:

  • Introduces a cancelledRef (via useRef) that is set to false when the effect runs and true on cleanup
  • Guards setState calls in both refreshBalance and fetchAssetMetadata callbacks with cancelledRef.current checks
  • Adds proper effect cleanup return function in the useEffect that triggers balance fetching

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// Format the balance
const balanceFormatted = formatUnits(balanceRaw, decimals);

if (cancelledRef.current) return "";
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cancellation guard at line 142 returns early from the try block, and the one at line 150 returns early from the catch block, but the finally block (line 160-162) still calls setIsFetchingBalance(false) unconditionally. Since finally always runs regardless of early returns, this state update will still fire after unmount/cancellation, undermining the purpose of these guards. You should add a cancelledRef.current check inside the finally block as well, or restructure so that the setIsFetchingBalance(false) call is placed before each return instead of in finally.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants