Skip to content

Commit

Permalink
fix(react-router): route context being undefined after a redirect (
Browse files Browse the repository at this point in the history
…#1599)

* fix(react-router): context being undefined after a redirect

* test: move to tsx

* test: test cases and fail intentionally fail it

* fix: refix it to pass the test cases
  • Loading branch information
SeanCassiere committed May 16, 2024
1 parent 19ede5c commit 843aca7
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 3 deletions.
3 changes: 1 addition & 2 deletions packages/react-router/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1600,11 +1600,10 @@ export class Router<
context: replaceEqualDeep(match.context, context),
abortController,
}
updateMatch(match.id, () => match)
} catch (err) {
handleSerialError(err, 'BEFORE_LOAD')
break
} finally {
updateMatch(match.id, () => match)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import { describe, expect, it } from 'vitest'
import { afterEach, describe, expect, it, vi } from 'vitest'

import {
createMemoryHistory,
createRootRoute,
createRootRouteWithContext,
createRoute,
createRouter,
redirect,
type RouterHistory,
} from '../src'

const mockFn1 = vi.fn()

afterEach(() => {
vi.clearAllMocks()
})

function createTestRouter(initialHistory?: RouterHistory) {
const history =
initialHistory ?? createMemoryHistory({ initialEntries: ['/'] })
Expand Down Expand Up @@ -455,3 +463,88 @@ describe('router.navigate navigation using layout routes resolves correctly', as
expect(router.state.location.pathname).toBe('/g/tkdodo')
})
})

function createContextRouter<TCtx extends Record<string, unknown>>(
initialEntries: string[],
mode: 'BEFORE_LOAD' | 'LOADER',
ctx: TCtx,
) {
const rootRoute = createRootRouteWithContext<TCtx>()()

let allow = false

const indexRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/',
beforeLoad: ({ context }) => {
if (!allow) {
allow = true
throw redirect({ to: '/about' })
}

if (mode === 'BEFORE_LOAD') {
mockFn1(context)
}
},
loader: ({ context }) => {
if (mode === 'LOADER') {
mockFn1(context)
}
},
})

const aboutRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/about',
})

const routeTree = rootRoute.addChildren([indexRoute, aboutRoute])

const router = createRouter({
routeTree,
history: createMemoryHistory({ initialEntries }),
context: ctx,
})

return router
}

describe('after redirect, the route context is passed to beforeLoad', () => {
it('should pass context to beforeLoad', async () => {
const ctx = {
name: 'Tanner',
}
const router = createContextRouter(['/'], 'BEFORE_LOAD', ctx)
await router.load()

expect(router.state.location.pathname).toBe('/about')
expect(mockFn1).toBeCalledTimes(0)

await router.navigate({ to: '/' })
await router.invalidate()

expect(router.state.location.pathname).toBe('/')
expect(mockFn1).toBeCalledTimes(1)
expect(mockFn1).toBeCalledWith(ctx)
})
})

describe('after redirect, the route context is passed to loader', () => {
it('should pass context to loader', async () => {
const ctx = {
name: 'Tanner',
}
const router = createContextRouter(['/'], 'LOADER', ctx)
await router.load()

expect(router.state.location.pathname).toBe('/about')
expect(mockFn1).toBeCalledTimes(0)

await router.navigate({ to: '/' })
await router.invalidate()

expect(router.state.location.pathname).toBe('/')
expect(mockFn1).toBeCalledTimes(1)
expect(mockFn1).toBeCalledWith(ctx)
})
})

0 comments on commit 843aca7

Please sign in to comment.