From d401bf9d97a556e7074b301997da6399a96e3f45 Mon Sep 17 00:00:00 2001 From: orca-bug-scan-bot Date: Sun, 31 May 2026 12:10:15 -0700 Subject: [PATCH] fix: address pr-bug-scan validated finding from #4243 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Defer pairScanRootRef dispose to a microtask; if a sibling branch remounts (mountedRef back to true), skip dispose — blocks branch-swap teardown. --- mobile/app/pair-scan.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/mobile/app/pair-scan.tsx b/mobile/app/pair-scan.tsx index 79314437b51..fbbc805900d 100644 --- a/mobile/app/pair-scan.tsx +++ b/mobile/app/pair-scan.tsx @@ -51,10 +51,22 @@ export default function PairScanScreen() { return } // Why: pairing attempts can outlive the visible route; dispose them when - // the scan screen detaches without a passive cleanup-only Effect. + // the scan screen detaches without a passive cleanup-only Effect. The + // dispose is deferred to a microtask because a permission-state flip + // unmounts one conditional branch and mounts another in the same + // commit — the unmounting branch's ref(null) fires before the new + // branch's ref(node), and we must not tear down an in-flight pairing + // attempt for what is really a sibling-branch swap inside the same + // mounted screen. mountedRef.current = false - activePairingAttemptRef.current?.dispose() - activePairingAttemptRef.current = null + const attempt = activePairingAttemptRef.current + queueMicrotask(() => { + if (mountedRef.current) return + attempt?.dispose() + if (activePairingAttemptRef.current === attempt) { + activePairingAttemptRef.current = null + } + }) }, []) const handleBarCodeScanned = useCallback(