fix(react-router): stop lazyRouteComponent flashing the error component while reloading - #8433
quanglam2807 wants to merge 1 commit into
Conversation
…nt while reloading `window.location.reload()` is asynchronous, so renders can still happen between the reload request and the document going away. Those renders re-read the sessionStorage guard, find it already set, and fall through to `throw error`, so the route's error component renders the import failure for a moment before the reload lands. Remember the reload request in the closure and keep suspending on later renders instead of re-evaluating the guard. The sessionStorage guard is untouched, so a chunk missing for any reason other than a new deployment still surfaces its error on the next page load rather than looping. Fixes TanStack#8377
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughChangesLazy route reload handling
Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix · Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to The reload re-entry behavior and existing anti-loop guard are covered by the changed regression tests. No merge-blocking risk is identified. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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 |
Fixes #8377.
🎯 Changes
lazyRouteComponentalready recovers from a stale chunk after a deploy: it sets asessionStorageguard, callswindow.location.reload()and suspends on a never-resolving promise.But
reload()is asynchronous anderroris never cleared, so any render between the reload request and the document going away re-enters theif (error)branch, finds the guard key already set, and falls through tothrow error. The route'serrorComponentrenders the import failure for a moment until the reload lands — and, for anyone wiring the router'sdefaultOnCatchto error tracking, the failure is reported as unhandled even though the user recovers.This remembers the reload request in the closure and keeps suspending on later renders instead of re-evaluating the guard:
The
sessionStorageguard is deliberately untouched. It exists to stop a reload loop when a chunk is missing for some reason other than a new deployment, and it still does: the closure flag only covers renders within the page that is already reloading, so a fresh page load still surfaces the error.Why this is worth fixing beyond the visual flash
Users do recover, so the flash is minor on its own. The problem is that a recovered failure becomes indistinguishable from an unrecovered one in error tracking. On one of our production apps this is the single largest source of client errors — ~400 events across ~93 issue groups (one per dead chunk URL, since the URL is in the message), at a deploy every 5–10 minutes. We spent a while assuming users were stranded on an error screen before the minified culprit frame showed they were being reloaded correctly the whole time. Details and breadcrumbs in #8377.
Tests
Added
packages/react-router/tests/issue-8377-lazy-chunk-reload-reentry.test.tsx, following the existingissue-NNNNconvention:expected TypeError: Failed to fetch dynamically im… to be an instance of Promise.Scope
React only. For reference, I checked the other two frameworks:
returns{ default: () => null }instead of throwing, but a second render still reachesthrow error. I left it out to keep this reviewable and because I can't exercise it as confidently — happy to add it here or in a follow-up if you'd prefer it in one go.attemptedReloadclosure flag, so it needs no change.✅ Checklist
Full
@tanstack/react-routersuite: 93 files, 1180 passed, 1 skipped, no type errors.🚀 Release Impact
Summary by CodeRabbit
Bug Fixes
Tests