fix(router): abandon a navigation whose navigator went away - #763
Merged
Conversation
linkTo checks assertIsReady once, then awaits the route preload, route validation, and any module promise loadRoute throws. Every one of those can outlive the tree that started the navigation: an auth gate that swaps Slot for Redirect unmounts the subtree mid-flight, and a host that hot-swaps an app by unmounting the React root and re-evaluating its bundle in the same realm leaves the closure holding a container ref that is detached for good. After the await, linkTo re-entered that ref unguarded (getRootState, getCurrentRoute, resetRoot, dispatch), and its post-dispatch poll called getCurrentRoute on a 16ms timer that outlives the tree outright. Every method on a detached ref logs "The 'navigation' object hasn't been initialized yet", so an orphaned navigation printed a react-navigation error into a console it no longer had anything to do with. Both sites now stop on !navigationRef.isReady(): there is no tree left to dispatch into and the ref never re-attaches for this navigation, so waiting or retrying would just spin. The post-await bail clears the loading state it set before the preload. linkToOrphaned.test.ts covers both: detaching the ref while linkTo is parked on its preload, and detaching it after a completed dispatch so the poll fires against nothing. Reverting either guard fails the matching test with the literal production string. Team-Machine-Session: m8354
|
🚅 Deployed to the one-pr-763 environment in onestack.dev
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
linkToawaits route preload and validation, and that work can outlive the tree that started the navigation. An auth gate swapping Slot for Redirect, or any host that hot-swaps an app by unmounting the React root and re-evaluating its bundle in the same realm, leaves the closure holding a container ref that is detached for good. Every method on a detached ref logsThe 'navigation' object hasn't been initialized yet.Two guards, both bailing rather than waiting, because the ref never re-attaches for an orphaned navigation:
linkToset before the preload, so nothing is left hanging.dismiss()/dismissAll()are deliberately untouched. Those are synchronous user actions where a not-ready ref is genuine misuse and the log is informative.The
clearTimeout(interval)->clearInterval(interval)change is consistency with the early return's teardown, not a fix: measured,clearTimeoutalready stopped the interval in both Chromium and node, which share one handle space.linkToOrphaned.test.tscovers both paths deterministically, detaching the ref synchronously afterreplace()so it lands beforelinkToresumes past its first await. Negative control: removing only the two guards and leavingclearIntervalin place reproduces the production string in both tests.Validation: full package suite 67 files / 575 tests,
bun run typecheck,bun run lint0 warnings 0 errors, all against the exact bytes on the branch.