Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ declare module '@tanstack/react-router' {
'/_layout': {
id: '/_layout'
path: ''
fullPath: '/'
fullPath: ''
preLoaderRoute: typeof LayoutRouteImport
parentRoute: typeof rootRouteImport
}
Expand Down Expand Up @@ -199,7 +199,7 @@ declare module '@tanstack/react-router' {
'/_layout/_layout-2': {
id: '/_layout/_layout-2'
path: ''
fullPath: '/'
fullPath: ''
preLoaderRoute: typeof LayoutLayout2RouteImport
parentRoute: typeof LayoutRoute
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ declare module '@tanstack/react-router' {
'/_layout': {
id: '/_layout'
path: ''
fullPath: '/'
fullPath: ''
preLoaderRoute: typeof LayoutRouteImport
parentRoute: typeof rootRouteImport
}
Expand Down Expand Up @@ -152,7 +152,7 @@ declare module '@tanstack/react-router' {
'/_layout/_layout-2': {
id: '/_layout/_layout-2'
path: ''
fullPath: '/'
fullPath: ''
preLoaderRoute: typeof LayoutLayout2RouteImport
parentRoute: typeof LayoutRoute
}
Expand Down
6 changes: 3 additions & 3 deletions e2e/vue-start/custom-basepath/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export interface FileRoutesByFullPath {
'/redirect/throw-it': typeof RedirectThrowItRoute
'/users/$userId': typeof UsersUserIdRoute
'/posts/': typeof PostsIndexRoute
'/redirect/': typeof RedirectIndexRoute
'/redirect': typeof RedirectIndexRoute
'/users/': typeof UsersIndexRoute
'/api/users/$userId': typeof ApiUsersUserIdRoute
'/posts/$postId/deep': typeof PostsPostIdDeepRoute
Expand Down Expand Up @@ -155,7 +155,7 @@ export interface FileRouteTypes {
| '/redirect/throw-it'
| '/users/$userId'
| '/posts/'
| '/redirect/'
| '/redirect'
| '/users/'
| '/api/users/$userId'
| '/posts/$postId/deep'
Expand Down Expand Up @@ -250,7 +250,7 @@ declare module '@tanstack/vue-router' {
'/redirect/': {
id: '/redirect/'
path: '/redirect'
fullPath: '/redirect/'
fullPath: '/redirect'
preLoaderRoute: typeof RedirectIndexRouteImport
parentRoute: typeof rootRouteImport
}
Expand Down
59 changes: 12 additions & 47 deletions packages/history/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@

export interface NavigateOptions {
ignoreBlocker?: boolean
/** When true, Transitioner should skip calling load() - commitLocation handles it */
skipTransitionerLoad?: boolean
}

/** Result of a navigation attempt (push/replace) */
export type NavigationResult = { type: 'SUCCESS' } | { type: 'BLOCKED' }

type SubscriberHistoryAction =
| {
type: Exclude<HistoryAction, 'GO'>
Expand All @@ -20,27 +15,18 @@ type SubscriberHistoryAction =
index: number
}

export type SubscriberArgs = {
type SubscriberArgs = {
location: HistoryLocation
action: SubscriberHistoryAction
navigateOpts?: NavigateOptions
}

export interface RouterHistory {
location: HistoryLocation
length: number
subscribers: Set<(opts: SubscriberArgs) => void>
subscribe: (cb: (opts: SubscriberArgs) => void) => () => void
push: (
path: string,
state?: any,
navigateOpts?: NavigateOptions,
) => Promise<NavigationResult>
replace: (
path: string,
state?: any,
navigateOpts?: NavigateOptions,
) => Promise<NavigationResult>
push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void
replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void
go: (index: number, navigateOpts?: NavigateOptions) => void
back: (navigateOpts?: NavigateOptions) => void
forward: (navigateOpts?: NavigateOptions) => void
Expand Down Expand Up @@ -70,21 +56,6 @@ export type ParsedHistoryState = HistoryState & {
key?: string // TODO: Remove in v2 - use __TSR_key instead
__TSR_key?: string
__TSR_index: number
/** Whether to reset scroll position on this navigation (default: true) */
__TSR_resetScroll?: boolean
/** Session id for cached TSR internals */
__TSR_sessionId?: string
/** Match snapshot for fast-path on back/forward navigation */
__TSR_matches?: {
routeIds: Array<string>
params: Record<string, string>
globalNotFoundRouteId?: string
searchStr?: string
validatedSearches?: Array<{
search: Record<string, unknown>
strictSearch: Record<string, unknown>
}>
}
}

type ShouldAllowNavigation = any
Expand Down Expand Up @@ -145,14 +116,9 @@ export function createHistory(opts: {
let location = opts.getLocation()
const subscribers = new Set<(opts: SubscriberArgs) => void>()

const notify = (
action: SubscriberHistoryAction,
navigateOpts?: NavigateOptions,
) => {
const notify = (action: SubscriberHistoryAction) => {
location = opts.getLocation()
subscribers.forEach((subscriber) =>
subscriber({ location, action, navigateOpts }),
)
subscribers.forEach((subscriber) => subscriber({ location, action }))
}

const handleIndexChange = (action: SubscriberHistoryAction) => {
Expand All @@ -164,11 +130,11 @@ export function createHistory(opts: {
task,
navigateOpts,
...actionInfo
}: TryNavigateArgs): Promise<NavigationResult> => {
}: TryNavigateArgs) => {
const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false
if (ignoreBlocker) {
task()
return { type: 'SUCCESS' }
return
}

const blockers = opts.getBlockers?.() ?? []
Expand All @@ -184,13 +150,12 @@ export function createHistory(opts: {
})
if (isBlocked) {
opts.onBlocked?.()
return { type: 'BLOCKED' }
return
}
}
}

task()
return { type: 'SUCCESS' }
}

return {
Expand All @@ -211,10 +176,10 @@ export function createHistory(opts: {
push: (path, state, navigateOpts) => {
const currentIndex = location.state[stateIndexKey]
state = assignKeyAndIndex(currentIndex + 1, state)
return tryNavigation({
tryNavigation({
task: () => {
opts.pushState(path, state)
notify({ type: 'PUSH' }, navigateOpts)
notify({ type: 'PUSH' })
},
navigateOpts,
type: 'PUSH',
Expand All @@ -225,10 +190,10 @@ export function createHistory(opts: {
replace: (path, state, navigateOpts) => {
const currentIndex = location.state[stateIndexKey]
state = assignKeyAndIndex(currentIndex, state)
return tryNavigation({
tryNavigation({
task: () => {
opts.replaceState(path, state)
notify({ type: 'REPLACE' }, navigateOpts)
notify({ type: 'REPLACE' })
},
navigateOpts,
type: 'REPLACE',
Expand Down
13 changes: 1 addition & 12 deletions packages/react-router/src/Transitioner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
import { useLayoutEffect, usePrevious } from './utils'
import { useRouter } from './useRouter'
import { useRouterState } from './useRouterState'
import type { SubscriberArgs } from '@tanstack/history'

export function Transitioner() {
const router = useRouter()
Expand Down Expand Up @@ -42,17 +41,7 @@ export function Transitioner() {
// Subscribe to location changes
// and try to load the new location
React.useEffect(() => {
const unsub = router.history.subscribe(
({ navigateOpts }: SubscriberArgs) => {
// If commitLocation initiated this navigation, it handles load() itself
if (navigateOpts?.skipTransitionerLoad) {
return
}

// External navigation (pop, direct history.push, etc): call load normally
router.load()
},
)
const unsub = router.history.subscribe(router.load)

const nextLocation = router.buildLocation({
to: router.latestLocation.pathname,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-router/tests/Matches.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ describe('matching on different param types', () => {

await act(() => render(<RouterProvider router={router} />))

await act(() => router.history.push(nav))
act(() => router.history.push(nav))

const paramsToCheck = await screen.findByTestId('params')
const matchesToCheck = await screen.findByTestId('matches')
Expand Down
50 changes: 15 additions & 35 deletions packages/react-router/tests/useNavigate.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
fireEvent,
render,
screen,
waitFor,
} from '@testing-library/react'

import { z } from 'zod'
Expand Down Expand Up @@ -1317,11 +1316,9 @@ test('when setting search params with 2 parallel navigate calls', async () => {
})

render(<RouterProvider router={router} />)
await waitFor(() => {
expect(router.state.location.search).toEqual({
param1: 'param1-default',
param2: 'param2-default',
})
expect(router.state.location.search).toEqual({
param1: 'param1-default',
param2: 'param2-default',
})

const postsButton = await screen.findByRole('button', { name: 'search' })
Expand All @@ -1330,12 +1327,7 @@ test('when setting search params with 2 parallel navigate calls', async () => {

expect(await screen.findByTestId('param1')).toHaveTextContent('foo')
expect(await screen.findByTestId('param2')).toHaveTextContent('bar')
await waitFor(() => {
expect(router.state.location.search).toEqual({
param1: 'foo',
param2: 'bar',
})
})
expect(router.state.location.search).toEqual({ param1: 'foo', param2: 'bar' })
const search = new URLSearchParams(window.location.search)
expect(search.get('param1')).toEqual('foo')
expect(search.get('param2')).toEqual('bar')
Expand Down Expand Up @@ -1455,27 +1447,21 @@ test.each([true, false])(

fireEvent.click(postButton)

await waitFor(() => {
expect(router.state.location.pathname).toBe(`/post${tail}`)
})
expect(router.state.location.pathname).toBe(`/post${tail}`)

const searchButton = await screen.findByTestId('search-btn')

fireEvent.click(searchButton)

await waitFor(() => {
expect(router.state.location.pathname).toBe(`/post${tail}`)
expect(router.state.location.search).toEqual({ param1: 'value1' })
})
expect(router.state.location.pathname).toBe(`/post${tail}`)
expect(router.state.location.search).toEqual({ param1: 'value1' })

const searchButton2 = await screen.findByTestId('search2-btn')

fireEvent.click(searchButton2)

await waitFor(() => {
expect(router.state.location.pathname).toBe(`/post${tail}`)
expect(router.state.location.search).toEqual({ param1: 'value2' })
})
expect(router.state.location.pathname).toBe(`/post${tail}`)
expect(router.state.location.search).toEqual({ param1: 'value2' })
},
)

Expand Down Expand Up @@ -1774,28 +1760,22 @@ test.each([true, false])(

fireEvent.click(detail1AddBtn)

await waitFor(() => {
expect(router.state.location.pathname).toBe(`/posts/id1/detail${tail}`)
expect(router.state.location.search).toEqual({ _test: true })
})
expect(router.state.location.pathname).toBe(`/posts/id1/detail${tail}`)
expect(router.state.location.search).toEqual({ _test: true })

const detail1RemoveBtn = await screen.findByTestId('detail-btn-remove-1')

fireEvent.click(detail1RemoveBtn)

await waitFor(() => {
expect(router.state.location.pathname).toBe(`/posts/id1/detail${tail}`)
expect(router.state.location.search).toEqual({})
})
expect(router.state.location.pathname).toBe(`/posts/id1/detail${tail}`)
expect(router.state.location.search).toEqual({})

const detail2AddBtn = await screen.findByTestId('detail-btn-add-2')

fireEvent.click(detail2AddBtn)

await waitFor(() => {
expect(router.state.location.pathname).toBe(`/posts/id1/detail${tail}`)
expect(router.state.location.search).toEqual({ _test: true })
})
expect(router.state.location.pathname).toBe(`/posts/id1/detail${tail}`)
expect(router.state.location.search).toEqual({ _test: true })
},
)

Expand Down
6 changes: 1 addition & 5 deletions packages/router-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,7 @@ export type {
CreateLazyFileRoute,
} from './fileRoute'

export type {
MatchSnapshot,
ParsedLocation,
ValidatedSearchEntry,
} from './location'
export type { ParsedLocation } from './location'
export type { Manifest, RouterManagedTag } from './manifest'
export { isMatch } from './Matches'
export type {
Expand Down
Loading
Loading