Skip to content

Commit e0dae9b

Browse files
committed
feat: provide location in onEnter/onStay/onLeave hooks
1 parent a119869 commit e0dae9b

File tree

8 files changed

+81
-12
lines changed

8 files changed

+81
-12
lines changed

docs/router/framework/react/api/router/RouteOptionsType.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -245,19 +245,19 @@ type loaderDeps = (opts: { search: TFullSearchSchema }) => Record<string, any>
245245
246246
### `onEnter` property
247247
248-
- Type: `(match: RouteMatch) => void`
248+
- Type: `(match: RouteMatch, { location }: { location: ParsedLocation<{}> }) => void`
249249
- Optional
250250
- A function that will be called when a route is matched and loaded after not being matched in the previous location.
251251
252252
### `onStay` property
253253
254-
- Type: `(match: RouteMatch) => void`
254+
- Type: `(match: RouteMatch, { location }: { location: ParsedLocation<{}> }) => void`
255255
- Optional
256256
- A function that will be called when a route is matched and loaded after being matched in the previous location.
257257
258258
### `onLeave` property
259259
260-
- Type: `(match: RouteMatch) => void`
260+
- Type: `(match: RouteMatch, { location }: { location: ParsedLocation<{}> }) => void`
261261
- Optional
262262
- A function that will be called when a route is no longer matched after being matched in the previous location.
263263

packages/react-router/src/router.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -2003,7 +2003,10 @@ export class Router<
20032003
] as const
20042004
).forEach(([matches, hook]) => {
20052005
matches.forEach((match) => {
2006-
this.looseRoutesById[match.routeId]!.options[hook]?.(match)
2006+
this.looseRoutesById[match.routeId]!.options[hook]?.(
2007+
match,
2008+
{ location: next },
2009+
)
20072010
})
20082011
})
20092012
})

packages/react-router/tests/route.test-d.tsx

+12-3
Original file line numberDiff line numberDiff line change
@@ -1321,9 +1321,18 @@ test('when creating a child route with context, search, params, loader, loaderDe
13211321
invoicePage: deps.search.page,
13221322
}),
13231323
loader: () => ({ detailLoader: 'detailResult' }) as const,
1324-
onEnter: (match) => expectTypeOf(match).toMatchTypeOf<TExpectedMatch>(),
1325-
onStay: (match) => expectTypeOf(match).toMatchTypeOf<TExpectedMatch>(),
1326-
onLeave: (match) => expectTypeOf(match).toMatchTypeOf<TExpectedMatch>(),
1324+
onEnter: (match, { location }) => {
1325+
expectTypeOf(match).toMatchTypeOf<TExpectedMatch>()
1326+
expectTypeOf(location).toMatchTypeOf<ParsedLocation<{}>>()
1327+
},
1328+
onStay: (match, { location }) => {
1329+
expectTypeOf(match).toMatchTypeOf<TExpectedMatch>()
1330+
expectTypeOf(location).toMatchTypeOf<ParsedLocation<{}>>()
1331+
},
1332+
onLeave: (match, { location }) => {
1333+
expectTypeOf(match).toMatchTypeOf<TExpectedMatch>()
1334+
expectTypeOf(location).toMatchTypeOf<ParsedLocation<{}>>()
1335+
},
13271336
})
13281337
})
13291338

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

+21
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,27 @@ describe('onEnter event', () => {
192192

193193
expect(fn).toHaveBeenCalledWith({ foo: 'bar' })
194194
})
195+
196+
it('should have location defined in router.load()', async () => {
197+
const fn = vi.fn()
198+
const rootRoute = createRootRoute()
199+
const indexRoute = createRoute({
200+
getParentRoute: () => rootRoute,
201+
path: '/',
202+
component: () => {
203+
return <h1>Index</h1>
204+
},
205+
onEnter: (_, { location: { href, pathname } }) => {
206+
fn({ href, pathname })
207+
},
208+
})
209+
const routeTree = rootRoute.addChildren([indexRoute])
210+
const router = createRouter({ routeTree })
211+
212+
await router.load()
213+
214+
expect(fn).toHaveBeenCalledWith({ href: '/', pathname: '/' })
215+
})
195216
})
196217

197218
describe('route.head', () => {

packages/router-core/src/route.ts

+3
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,7 @@ export interface UpdatableRouteOptions<
865865
>,
866866
TLoaderDeps
867867
>,
868+
{ location }: { location: ParsedLocation<{}> },
868869
) => void
869870
onStay?: (
870871
match: RouteMatch<
@@ -881,6 +882,7 @@ export interface UpdatableRouteOptions<
881882
>,
882883
TLoaderDeps
883884
>,
885+
{ location }: { location: ParsedLocation<{}> },
884886
) => void
885887
onLeave?: (
886888
match: RouteMatch<
@@ -897,6 +899,7 @@ export interface UpdatableRouteOptions<
897899
>,
898900
TLoaderDeps
899901
>,
902+
{ location }: { location: ParsedLocation<{}> },
900903
) => void
901904
headers?: (ctx: {
902905
loaderData: ResolveLoaderData<TLoaderFn>

packages/solid-router/src/router.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ export class Router<
791791
if (
792792
typeof window !== 'undefined' &&
793793
'CSS' in window &&
794-
typeof window.CSS?.supports === 'function'
794+
typeof window.CSS.supports === 'function'
795795
) {
796796
this.isViewTransitionTypesSupported = window.CSS.supports(
797797
'selector(:active-view-transition-type(a)',
@@ -1977,7 +1977,10 @@ export class Router<
19771977
] as const
19781978
).forEach(([matches, hook]) => {
19791979
matches.forEach((match) => {
1980-
this.looseRoutesById[match.routeId]!.options[hook]?.(match)
1980+
this.looseRoutesById[match.routeId]!.options[hook]?.(
1981+
match,
1982+
{ location: next },
1983+
)
19811984
})
19821985
})
19831986
})

packages/solid-router/tests/route.test-d.tsx

+12-3
Original file line numberDiff line numberDiff line change
@@ -1286,9 +1286,18 @@ test('when creating a child route with context, search, params, loader, loaderDe
12861286
invoicePage: deps.search.page,
12871287
}),
12881288
loader: () => ({ detailLoader: 'detailResult' }) as const,
1289-
onEnter: (match) => expectTypeOf(match).toMatchTypeOf<TExpectedMatch>(),
1290-
onStay: (match) => expectTypeOf(match).toMatchTypeOf<TExpectedMatch>(),
1291-
onLeave: (match) => expectTypeOf(match).toMatchTypeOf<TExpectedMatch>(),
1289+
onEnter: (match, { location }) => {
1290+
expectTypeOf(match).toMatchTypeOf<TExpectedMatch>()
1291+
expectTypeOf(location).toMatchTypeOf<ParsedLocation<{}>>()
1292+
},
1293+
onStay: (match, { location }) => {
1294+
expectTypeOf(match).toMatchTypeOf<TExpectedMatch>()
1295+
expectTypeOf(location).toMatchTypeOf<ParsedLocation<{}>>()
1296+
},
1297+
onLeave: (match, { location }) => {
1298+
expectTypeOf(match).toMatchTypeOf<TExpectedMatch>()
1299+
expectTypeOf(location).toMatchTypeOf<ParsedLocation<{}>>()
1300+
},
12921301
})
12931302
})
12941303

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

+21
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,27 @@ describe('onEnter event', () => {
191191

192192
expect(fn).toHaveBeenCalledWith({ foo: 'bar' })
193193
})
194+
195+
it('should have location defined in router.load()', async () => {
196+
const fn = vi.fn()
197+
const rootRoute = createRootRoute()
198+
const indexRoute = createRoute({
199+
getParentRoute: () => rootRoute,
200+
path: '/',
201+
component: () => {
202+
return <h1>Index</h1>
203+
},
204+
onEnter: (_, { location: { href, pathname } }) => {
205+
fn({ href, pathname })
206+
},
207+
})
208+
const routeTree = rootRoute.addChildren([indexRoute])
209+
const router = createRouter({ routeTree })
210+
211+
await router.load()
212+
213+
expect(fn).toHaveBeenCalledWith({ href: '/', pathname: '/' })
214+
})
194215
})
195216

196217
describe('route.head', () => {

0 commit comments

Comments
 (0)