Skip to content

Commit 1d1b188

Browse files
authored
useRoute: return type-safe handle (#14462)
* fix typo * useRoute: return type-safe `handle`
1 parent 4a37a95 commit 1d1b188

File tree

3 files changed

+51
-5
lines changed

3 files changed

+51
-5
lines changed

.changeset/khaki-dingos-kiss.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
useRoute: return type-safe `handle`
6+
7+
For example:
8+
9+
```ts
10+
// app/routes/admin.tsx
11+
const handle = { hello: "world" };
12+
```
13+
14+
```ts
15+
// app/routes/some-other-route.tsx
16+
export default function Component() {
17+
const admin = useRoute("routes/admin");
18+
if (!admin) throw new Error("Not nested within 'routes/admin'");
19+
console.log(admin.handle);
20+
// ^? { hello: string }
21+
}
22+
```

integration/use-route-test.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ test.use({
2727
"app/root.tsx": tsx`
2828
import { Outlet } from "react-router"
2929
30+
export const handle = { rootHandle: "root/handle" }
3031
export const loader = () => ({ rootLoader: "root/loader" })
3132
export const action = () => ({ rootAction: "root/action" })
3233
@@ -42,6 +43,7 @@ test.use({
4243
"app/routes/parent.tsx": tsx`
4344
import { Outlet } from "react-router"
4445
46+
export const handle = { parentHandle: "parent/handle" }
4547
export const loader = () => ({ parentLoader: "parent/loader" })
4648
export const action = () => ({ parentAction: "parent/action" })
4749
@@ -59,21 +61,38 @@ test.use({
5961
6062
import type { Expect, Equal } from "../expect-type"
6163
64+
export const handle = { currentHandle: "current/handle" }
6265
export const loader = () => ({ currentLoader: "current/loader" })
6366
export const action = () => ({ currentAction: "current/action" })
6467
6568
export default function Component() {
6669
const current = useRoute()
67-
type Test1 = Expect<Equal<typeof current, { loaderData: unknown, actionData: unknown }>>
70+
type Test1 = Expect<Equal<typeof current, {
71+
handle: unknown,
72+
loaderData: unknown,
73+
actionData: unknown,
74+
}>>
6875
6976
const root = useRoute("root")
70-
type Test2 = Expect<Equal<typeof root, { loaderData: { rootLoader: string } | undefined, actionData: { rootAction: string } | undefined }>>
77+
type Test2 = Expect<Equal<typeof root, {
78+
handle: { rootHandle: string },
79+
loaderData: { rootLoader: string } | undefined,
80+
actionData: { rootAction: string } | undefined,
81+
}>>
7182
7283
const parent = useRoute("routes/parent")
73-
type Test3 = Expect<Equal<typeof parent, { loaderData: { parentLoader: string } | undefined, actionData: { parentAction: string } | undefined } | undefined>>
84+
type Test3 = Expect<Equal<typeof parent, {
85+
handle: { parentHandle: string },
86+
loaderData: { parentLoader: string } | undefined,
87+
actionData: { parentAction: string } | undefined
88+
} | undefined>>
7489
7590
const other = useRoute("routes/other")
76-
type Test4 = Expect<Equal<typeof other, { loaderData: { otherLoader: string } | undefined, actionData: { otherAction: string } | undefined } | undefined>>
91+
type Test4 = Expect<Equal<typeof other, {
92+
handle: { otherHandle: string },
93+
loaderData: { otherLoader: string } | undefined,
94+
actionData: { otherAction: string } | undefined,
95+
} | undefined>>
7796
7897
return (
7998
<>
@@ -87,6 +106,7 @@ test.use({
87106
}
88107
`,
89108
"app/routes/other.tsx": tsx`
109+
export const handle = { otherHandle: "other/handle" }
90110
export const loader = () => ({ otherLoader: "other/loader" })
91111
export const action = () => ({ otherAction: "other/action" })
92112

packages/react-router/lib/hooks.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1855,6 +1855,9 @@ type UseRouteResult<Args extends UseRouteArgs> =
18551855
never;
18561856

18571857
type UseRoute<RouteId extends keyof RouteModules | unknown> = {
1858+
handle: RouteId extends keyof RouteModules
1859+
? RouteModules[RouteId]["handle"]
1860+
: unknown;
18581861
loaderData: RouteId extends keyof RouteModules
18591862
? GetLoaderData<RouteModules[RouteId]> | undefined
18601863
: unknown;
@@ -1871,11 +1874,12 @@ export function useRoute<Args extends UseRouteArgs>(
18711874
);
18721875
const id: keyof RouteModules = args[0] ?? currentRouteId;
18731876

1874-
const state = useDataRouterState(DataRouterStateHook.UseRouteLoaderData);
1877+
const state = useDataRouterState(DataRouterStateHook.UseRoute);
18751878
const route = state.matches.find(({ route }) => route.id === id);
18761879

18771880
if (route === undefined) return undefined as UseRouteResult<Args>;
18781881
return {
1882+
handle: route.route.handle,
18791883
loaderData: state.loaderData[id],
18801884
actionData: state.actionData?.[id],
18811885
} as UseRouteResult<Args>;

0 commit comments

Comments
 (0)