Skip to content

Commit 942f0ff

Browse files
authored
fix(router-core): handle AbortError in router execution flow (#4570)
1 parent 4afc1f1 commit 942f0ff

File tree

3 files changed

+64
-1
lines changed

3 files changed

+64
-1
lines changed

packages/react-router/tests/loaders.test.tsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ test('reproducer #4546', async () => {
430430
component: () => {
431431
return (
432432
<>
433-
<div className="p-2 flex gap-2 text-lg">
433+
<div className="flex gap-2 p-2 text-lg">
434434
<Link
435435
data-testid="link-to-index"
436436
to="/"
@@ -730,6 +730,33 @@ test('clears pendingTimeout when match resolves', async () => {
730730
expect(fooPendingComponentOnMountMock).not.toHaveBeenCalled()
731731
})
732732

733+
test('throw abortError from loader upon initial load with basepath', async () => {
734+
window.history.replaceState(null, 'root', '/app')
735+
const rootRoute = createRootRoute({})
736+
737+
const indexRoute = createRoute({
738+
getParentRoute: () => rootRoute,
739+
path: '/',
740+
loader: async () => {
741+
return Promise.reject(new DOMException('Aborted', 'AbortError'))
742+
},
743+
component: () => <div>Index route content</div>,
744+
errorComponent: () => (
745+
<div data-testid="index-error">indexErrorComponent</div>
746+
),
747+
})
748+
749+
const routeTree = rootRoute.addChildren([indexRoute])
750+
const router = createRouter({ routeTree, history, basepath: '/app' })
751+
752+
render(<RouterProvider router={router} />)
753+
754+
const indexElement = await screen.findByText('Index route content')
755+
expect(indexElement).toBeInTheDocument()
756+
expect(screen.queryByTestId('index-error')).not.toBeInTheDocument()
757+
expect(window.location.pathname.startsWith('/app')).toBe(true)
758+
})
759+
733760
test('cancelMatches after pending timeout', async () => {
734761
function getPendingComponent(onMount: () => void) {
735762
const PendingComponent = () => {

packages/router-core/src/load-matches.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,15 @@ const runLoader = async (
699699
} catch (e) {
700700
let error = e
701701

702+
if ((error as any)?.name === 'AbortError') {
703+
inner.updateMatch(matchId, (prev) => ({
704+
...prev,
705+
status: prev.status === 'pending' ? 'success' : prev.status,
706+
isFetching: false,
707+
}))
708+
return
709+
}
710+
702711
const pendingPromise = match._nonReactive.minPendingPromise
703712
if (pendingPromise) await pendingPromise
704713

packages/solid-router/tests/loaders.test.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,33 @@ test('throw error from beforeLoad when navigating to route', async () => {
320320
expect(indexElement).toBeInTheDocument()
321321
})
322322

323+
test('throw abortError from loader upon initial load with basepath', async () => {
324+
window.history.replaceState(null, 'root', '/app')
325+
const rootRoute = createRootRoute({})
326+
327+
const indexRoute = createRoute({
328+
getParentRoute: () => rootRoute,
329+
path: '/',
330+
loader: async () => {
331+
return Promise.reject(new DOMException('Aborted', 'AbortError'))
332+
},
333+
component: () => <div>Index route content</div>,
334+
errorComponent: () => (
335+
<div data-testid="index-error">indexErrorComponent</div>
336+
),
337+
})
338+
339+
const routeTree = rootRoute.addChildren([indexRoute])
340+
const router = createRouter({ routeTree, basepath: '/app' })
341+
342+
render(() => <RouterProvider router={router} />)
343+
344+
const indexElement = await screen.findByText('Index route content')
345+
expect(indexElement).toBeInTheDocument()
346+
expect(screen.queryByTestId('index-error')).not.toBeInTheDocument()
347+
expect(window.location.pathname.startsWith('/app')).toBe(true)
348+
})
349+
323350
test('reproducer #4245', async () => {
324351
const LOADER_WAIT_TIME = 500
325352
const rootRoute = createRootRoute({})

0 commit comments

Comments
 (0)