fix: prevent rapid double-tap from pushing duplicate history entries#442
Conversation
The useTransition guard alone was insufficient because isPending is an async state update — a second tap could arrive before React re-renders. A ref-based lock (set synchronously on click, cleared only when pathname actually changes via useEffect) ensures no second navigation can start while one is in-flight.
📝 WalkthroughWalkthroughMobileNav switches from Next.js ChangesMobile Navigation Race Condition Prevention
Possibly related PRs
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@components/mobile-nav.tsx`:
- Around line 33-35: The lock (navigatingTo.current) is only cleared inside the
pathname useEffect, so if a push is interrupted and the URL doesn't change it
stays non-null; modify the navigation handler(s) that set navigatingTo.current
(e.g., the tap/click handler that calls router.push or navigate) to always clear
navigatingTo.current in a finally block after the navigation promise settles,
and add a short fallback timeout (e.g., 2–5s) to reset navigatingTo.current to
null if the navigation neither resolves nor changes pathname; keep the existing
useEffect([pathname]) clear as well for the normal case.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
Closes #328
Problem
Rapidly tapping two nav tabs in succession can push two routes onto the history stack before the first one completes, corrupting the back-button history on mobile.
Root Cause
The previous
useTransitionguard was insufficient —isPendingis an async React state update. A second tap could arrive before React re-renders withisPending=true, and there was no synchronous lock preventing the secondrouter.pushfrom executing.Fix (
components/mobile-nav.tsx)<Link>with<button>+handleNav()handler for full navigation controlnavigatingTo) is set on click and only cleared whenpathnameactually changes (viauseEffect), preventing any second navigation while one is in-flightuseTransitionprovides theisPendingflag to visually disable buttons during navigationpathname === hrefguard skips re-pushing the current pageCloses #328
Summary by CodeRabbit