Skip to content

Commit

Permalink
fix(react-router): properly handle redirects when thrown during paral…
Browse files Browse the repository at this point in the history
…lel preload (#3103)

fixes #3097
  • Loading branch information
schiller-manuel authored Jan 3, 2025
1 parent 0718fb8 commit 224a261
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 1 deletion.
80 changes: 79 additions & 1 deletion e2e/react-router/basic-file-based/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ import { Route as groupInsideImport } from './routes/(group)/inside'
import { Route as groupLayoutImport } from './routes/(group)/_layout'
import { Route as anotherGroupOnlyrouteinsideImport } from './routes/(another-group)/onlyrouteinside'
import { Route as RedirectTargetIndexImport } from './routes/redirect/$target/index'
import { Route as RedirectPreloadThirdImport } from './routes/redirect/preload/third'
import { Route as RedirectPreloadSecondImport } from './routes/redirect/preload/second'
import { Route as RedirectPreloadFirstImport } from './routes/redirect/preload/first'
import { Route as RedirectTargetViaLoaderImport } from './routes/redirect/$target/via-loader'
import { Route as RedirectTargetViaBeforeLoadImport } from './routes/redirect/$target/via-beforeLoad'
import { Route as PostsPostIdEditImport } from './routes/posts_.$postId.edit'
Expand Down Expand Up @@ -151,6 +154,24 @@ const RedirectTargetIndexRoute = RedirectTargetIndexImport.update({
getParentRoute: () => RedirectTargetRoute,
} as any)

const RedirectPreloadThirdRoute = RedirectPreloadThirdImport.update({
id: '/redirect/preload/third',
path: '/redirect/preload/third',
getParentRoute: () => rootRoute,
} as any)

const RedirectPreloadSecondRoute = RedirectPreloadSecondImport.update({
id: '/redirect/preload/second',
path: '/redirect/preload/second',
getParentRoute: () => rootRoute,
} as any)

const RedirectPreloadFirstRoute = RedirectPreloadFirstImport.update({
id: '/redirect/preload/first',
path: '/redirect/preload/first',
getParentRoute: () => rootRoute,
} as any)

const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderImport.update({
id: '/via-loader',
path: '/via-loader',
Expand Down Expand Up @@ -366,6 +387,27 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof RedirectTargetViaLoaderImport
parentRoute: typeof RedirectTargetImport
}
'/redirect/preload/first': {
id: '/redirect/preload/first'
path: '/redirect/preload/first'
fullPath: '/redirect/preload/first'
preLoaderRoute: typeof RedirectPreloadFirstImport
parentRoute: typeof rootRoute
}
'/redirect/preload/second': {
id: '/redirect/preload/second'
path: '/redirect/preload/second'
fullPath: '/redirect/preload/second'
preLoaderRoute: typeof RedirectPreloadSecondImport
parentRoute: typeof rootRoute
}
'/redirect/preload/third': {
id: '/redirect/preload/third'
path: '/redirect/preload/third'
fullPath: '/redirect/preload/third'
preLoaderRoute: typeof RedirectPreloadThirdImport
parentRoute: typeof rootRoute
}
'/redirect/$target/': {
id: '/redirect/$target/'
path: '/'
Expand Down Expand Up @@ -481,6 +523,9 @@ export interface FileRoutesByFullPath {
'/posts/$postId/edit': typeof PostsPostIdEditRoute
'/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute
'/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute
'/redirect/preload/first': typeof RedirectPreloadFirstRoute
'/redirect/preload/second': typeof RedirectPreloadSecondRoute
'/redirect/preload/third': typeof RedirectPreloadThirdRoute
'/redirect/$target/': typeof RedirectTargetIndexRoute
}

Expand All @@ -504,6 +549,9 @@ export interface FileRoutesByTo {
'/posts/$postId/edit': typeof PostsPostIdEditRoute
'/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute
'/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute
'/redirect/preload/first': typeof RedirectPreloadFirstRoute
'/redirect/preload/second': typeof RedirectPreloadSecondRoute
'/redirect/preload/third': typeof RedirectPreloadThirdRoute
'/redirect/$target': typeof RedirectTargetIndexRoute
}

Expand Down Expand Up @@ -533,6 +581,9 @@ export interface FileRoutesById {
'/posts_/$postId/edit': typeof PostsPostIdEditRoute
'/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute
'/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute
'/redirect/preload/first': typeof RedirectPreloadFirstRoute
'/redirect/preload/second': typeof RedirectPreloadSecondRoute
'/redirect/preload/third': typeof RedirectPreloadThirdRoute
'/redirect/$target/': typeof RedirectTargetIndexRoute
}

Expand Down Expand Up @@ -560,6 +611,9 @@ export interface FileRouteTypes {
| '/posts/$postId/edit'
| '/redirect/$target/via-beforeLoad'
| '/redirect/$target/via-loader'
| '/redirect/preload/first'
| '/redirect/preload/second'
| '/redirect/preload/third'
| '/redirect/$target/'
fileRoutesByTo: FileRoutesByTo
to:
Expand All @@ -582,6 +636,9 @@ export interface FileRouteTypes {
| '/posts/$postId/edit'
| '/redirect/$target/via-beforeLoad'
| '/redirect/$target/via-loader'
| '/redirect/preload/first'
| '/redirect/preload/second'
| '/redirect/preload/third'
| '/redirect/$target'
id:
| '__root__'
Expand Down Expand Up @@ -609,6 +666,9 @@ export interface FileRouteTypes {
| '/posts_/$postId/edit'
| '/redirect/$target/via-beforeLoad'
| '/redirect/$target/via-loader'
| '/redirect/preload/first'
| '/redirect/preload/second'
| '/redirect/preload/third'
| '/redirect/$target/'
fileRoutesById: FileRoutesById
}
Expand All @@ -626,6 +686,9 @@ export interface RootRouteChildren {
StructuralSharingEnabledRoute: typeof StructuralSharingEnabledRoute
RedirectIndexRoute: typeof RedirectIndexRoute
PostsPostIdEditRoute: typeof PostsPostIdEditRoute
RedirectPreloadFirstRoute: typeof RedirectPreloadFirstRoute
RedirectPreloadSecondRoute: typeof RedirectPreloadSecondRoute
RedirectPreloadThirdRoute: typeof RedirectPreloadThirdRoute
}

const rootRouteChildren: RootRouteChildren = {
Expand All @@ -641,6 +704,9 @@ const rootRouteChildren: RootRouteChildren = {
StructuralSharingEnabledRoute: StructuralSharingEnabledRoute,
RedirectIndexRoute: RedirectIndexRoute,
PostsPostIdEditRoute: PostsPostIdEditRoute,
RedirectPreloadFirstRoute: RedirectPreloadFirstRoute,
RedirectPreloadSecondRoute: RedirectPreloadSecondRoute,
RedirectPreloadThirdRoute: RedirectPreloadThirdRoute,
}

export const routeTree = rootRoute
Expand All @@ -664,7 +730,10 @@ export const routeTree = rootRoute
"/redirect/$target",
"/structural-sharing/$enabled",
"/redirect/",
"/posts_/$postId/edit"
"/posts_/$postId/edit",
"/redirect/preload/first",
"/redirect/preload/second",
"/redirect/preload/third"
]
},
"/": {
Expand Down Expand Up @@ -776,6 +845,15 @@ export const routeTree = rootRoute
"filePath": "redirect/$target/via-loader.tsx",
"parent": "/redirect/$target"
},
"/redirect/preload/first": {
"filePath": "redirect/preload/first.tsx"
},
"/redirect/preload/second": {
"filePath": "redirect/preload/second.tsx"
},
"/redirect/preload/third": {
"filePath": "redirect/preload/third.tsx"
},
"/redirect/$target/": {
"filePath": "redirect/$target/index.tsx",
"parent": "/redirect/$target"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Link, createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/redirect/preload/first')({
component: RouteComponent,
})

function RouteComponent() {
return (
<div data-testid="first">
<Link
from={Route.fullPath}
to="../second"
activeProps={{
className: 'font-bold',
}}
preload="intent"
data-testid={'link'}
>
go to second
</Link>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createFileRoute, redirect } from '@tanstack/react-router'

export const Route = createFileRoute('/redirect/preload/second')({
loader: async () => {
await new Promise((r) => setTimeout(r, 1000))
throw redirect({ from: Route.fullPath, to: '../third' })
},
component: RouteComponent,
pendingComponent: () => <p>second pending</p>,
})

function RouteComponent() {
return <div data-testid="second">Hello "/redirect/preload/second"!</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/redirect/preload/third')({
component: RouteComponent,
})

function RouteComponent() {
return <div data-testid="third">Hello "/redirect/preload/third"!</div>
}
9 changes: 9 additions & 0 deletions e2e/react-router/basic-file-based/tests/redirect.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,13 @@ test.describe('redirects', () => {
expect(page.url()).toBe(url)
})
})

test('regression test for #3097', async ({ page }) => {
await page.goto(`/redirect/preload/first`)
const link = page.getByTestId(`link`)
await link.focus()
await link.click()
await page.waitForURL('/redirect/preload/third')
await expect(page.getByTestId(`third`)).toBeInViewport()
})
})
4 changes: 4 additions & 0 deletions packages/react-router/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2492,6 +2492,10 @@ export class Router<

if (prevLoaderPromise) {
await prevLoaderPromise
const match = this.getMatch(matchId)!
if (match.error) {
handleRedirectAndNotFound(match, match.error)
}
} else {
const parentMatchPromise = matchPromises[index - 1] as any
const route = this.looseRoutesById[routeId]!
Expand Down

0 comments on commit 224a261

Please sign in to comment.