diff --git a/docs/router/framework/react/api/router/RouteOptionsType.md b/docs/router/framework/react/api/router/RouteOptionsType.md index 92510684ea0..46511c94c51 100644 --- a/docs/router/framework/react/api/router/RouteOptionsType.md +++ b/docs/router/framework/react/api/router/RouteOptionsType.md @@ -245,19 +245,19 @@ type loaderDeps = (opts: { search: TFullSearchSchema }) => Record ### `onEnter` property -- Type: `(match: RouteMatch) => void` +- Type: `(match: RouteMatch, { location }: { location: ParsedLocation<{}> }) => void` - Optional - A function that will be called when a route is matched and loaded after not being matched in the previous location. ### `onStay` property -- Type: `(match: RouteMatch) => void` +- Type: `(match: RouteMatch, { location }: { location: ParsedLocation<{}> }) => void` - Optional - A function that will be called when a route is matched and loaded after being matched in the previous location. ### `onLeave` property -- Type: `(match: RouteMatch) => void` +- Type: `(match: RouteMatch, { location }: { location: ParsedLocation<{}> }) => void` - Optional - A function that will be called when a route is no longer matched after being matched in the previous location. diff --git a/packages/react-router/src/router.ts b/packages/react-router/src/router.ts index 046048727e7..5a0b7e0ac74 100644 --- a/packages/react-router/src/router.ts +++ b/packages/react-router/src/router.ts @@ -1576,7 +1576,10 @@ export class Router< ] as const ).forEach(([matches, hook]) => { matches.forEach((match) => { - this.looseRoutesById[match.routeId]!.options[hook]?.(match) + this.looseRoutesById[match.routeId]!.options[hook]?.( + match, + { location: next }, + ) }) }) }) diff --git a/packages/react-router/tests/route.test-d.tsx b/packages/react-router/tests/route.test-d.tsx index 2183cf4af71..2af1928e112 100644 --- a/packages/react-router/tests/route.test-d.tsx +++ b/packages/react-router/tests/route.test-d.tsx @@ -1321,9 +1321,18 @@ test('when creating a child route with context, search, params, loader, loaderDe invoicePage: deps.search.page, }), loader: () => ({ detailLoader: 'detailResult' }) as const, - onEnter: (match) => expectTypeOf(match).toMatchTypeOf(), - onStay: (match) => expectTypeOf(match).toMatchTypeOf(), - onLeave: (match) => expectTypeOf(match).toMatchTypeOf(), + onEnter: (match, { location }) => { + expectTypeOf(match).toMatchTypeOf() + expectTypeOf(location).toMatchTypeOf>() + }, + onStay: (match, { location }) => { + expectTypeOf(match).toMatchTypeOf() + expectTypeOf(location).toMatchTypeOf>() + }, + onLeave: (match, { location }) => { + expectTypeOf(match).toMatchTypeOf() + expectTypeOf(location).toMatchTypeOf>() + }, }) }) diff --git a/packages/react-router/tests/route.test.tsx b/packages/react-router/tests/route.test.tsx index 3a7cbee0bc6..4fb6b48efae 100644 --- a/packages/react-router/tests/route.test.tsx +++ b/packages/react-router/tests/route.test.tsx @@ -192,6 +192,27 @@ describe('onEnter event', () => { expect(fn).toHaveBeenCalledWith({ foo: 'bar' }) }) + + it('should have location defined in router.load()', async () => { + const fn = vi.fn() + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () => { + return

Index

+ }, + onEnter: (_, { location: { href, pathname } }) => { + fn({ href, pathname }) + }, + }) + const routeTree = rootRoute.addChildren([indexRoute]) + const router = createRouter({ routeTree }) + + await router.load() + + expect(fn).toHaveBeenCalledWith({ href: '/', pathname: '/' }) + }) }) describe('route.head', () => { diff --git a/packages/router-core/src/route.ts b/packages/router-core/src/route.ts index 43e72017d49..fe65af4129d 100644 --- a/packages/router-core/src/route.ts +++ b/packages/router-core/src/route.ts @@ -8,7 +8,12 @@ import type { RouteMatch, } from './Matches' import type { RootRouteId } from './root' -import type { ParseRoute, RouteById, RoutePaths } from './routeInfo' +import type { + FullSearchSchema, + ParseRoute, + RouteById, + RoutePaths, +} from './routeInfo' import type { AnyRouter, RegisteredRouter } from './router' import type { BuildLocationFn, NavigateFn } from './RouterProvider' import type { @@ -1032,6 +1037,13 @@ export interface UpdatableRouteOptions< >, TLoaderDeps >, + { + location, + }: { + location: ParsedLocation< + ResolveFullSearchSchema + > + }, ) => void onStay?: ( match: RouteMatch< @@ -1048,6 +1060,13 @@ export interface UpdatableRouteOptions< >, TLoaderDeps >, + { + location, + }: { + location: ParsedLocation< + ResolveFullSearchSchema + > + }, ) => void onLeave?: ( match: RouteMatch< @@ -1064,6 +1083,7 @@ export interface UpdatableRouteOptions< >, TLoaderDeps >, + { location }: { location: ParsedLocation> }, ) => void headers?: (ctx: { loaderData: ResolveLoaderData diff --git a/packages/solid-router/src/router.ts b/packages/solid-router/src/router.ts index 0bdf0ffb19a..bf0c7ba6801 100644 --- a/packages/solid-router/src/router.ts +++ b/packages/solid-router/src/router.ts @@ -1570,7 +1570,10 @@ export class Router< ] as const ).forEach(([matches, hook]) => { matches.forEach((match) => { - this.looseRoutesById[match.routeId]!.options[hook]?.(match) + this.looseRoutesById[match.routeId]!.options[hook]?.( + match, + { location: next }, + ) }) }) }) diff --git a/packages/solid-router/tests/route.test-d.tsx b/packages/solid-router/tests/route.test-d.tsx index fc58b779ab6..00ebed5e863 100644 --- a/packages/solid-router/tests/route.test-d.tsx +++ b/packages/solid-router/tests/route.test-d.tsx @@ -1286,9 +1286,18 @@ test('when creating a child route with context, search, params, loader, loaderDe invoicePage: deps.search.page, }), loader: () => ({ detailLoader: 'detailResult' }) as const, - onEnter: (match) => expectTypeOf(match).toMatchTypeOf(), - onStay: (match) => expectTypeOf(match).toMatchTypeOf(), - onLeave: (match) => expectTypeOf(match).toMatchTypeOf(), + onEnter: (match, { location }) => { + expectTypeOf(match).toMatchTypeOf() + expectTypeOf(location).toMatchTypeOf>() + }, + onStay: (match, { location }) => { + expectTypeOf(match).toMatchTypeOf() + expectTypeOf(location).toMatchTypeOf>() + }, + onLeave: (match, { location }) => { + expectTypeOf(match).toMatchTypeOf() + expectTypeOf(location).toMatchTypeOf>() + }, }) }) diff --git a/packages/solid-router/tests/route.test.tsx b/packages/solid-router/tests/route.test.tsx index 7a8f776a61a..11718c9f098 100644 --- a/packages/solid-router/tests/route.test.tsx +++ b/packages/solid-router/tests/route.test.tsx @@ -191,6 +191,27 @@ describe('onEnter event', () => { expect(fn).toHaveBeenCalledWith({ foo: 'bar' }) }) + + it('should have location defined in router.load()', async () => { + const fn = vi.fn() + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () => { + return

Index

+ }, + onEnter: (_, { location: { href, pathname } }) => { + fn({ href, pathname }) + }, + }) + const routeTree = rootRoute.addChildren([indexRoute]) + const router = createRouter({ routeTree }) + + await router.load() + + expect(fn).toHaveBeenCalledWith({ href: '/', pathname: '/' }) + }) }) describe('route.head', () => {