Skip to content

Commit 9147bf7

Browse files
authored
Fix types for UIMatch to reflect data may be undefined (#12206)
1 parent 202e1d1 commit 9147bf7

File tree

2 files changed

+41
-5
lines changed

2 files changed

+41
-5
lines changed

.changeset/thick-snails-compete.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Fix types for `UIMatch` to reflect that the `loaderData`/`data` properties may be `undefined`
6+
7+
- When an `ErrorBoundary` is being rendered, not all active matches will have loader data available, since it may have been their `loader` that threw to trigger the boundary
8+
- The `UIMatch.data` type was not correctly handing this and would always reflect the presence of data, leading to the unexpected runtime errors when an `ErrorBoundary` was rendered
9+
- ⚠️ This may cause some type errors to show up in your code for unguarded `match.data` accesses - you should properly guard for `undefined` values in those scenarios.
10+
11+
```tsx
12+
// app/root.tsx
13+
export function loader() {
14+
someFunctionThatThrows(); // ❌ Throws an Error
15+
return { title: "My Title" };
16+
}
17+
18+
export function Layout({ children }: { children: React.ReactNode }) {
19+
let matches = useMatches();
20+
let rootMatch = matches[0] as UIMatch<Awaited<ReturnType<typeof loader>>>;
21+
// ^ rootMatch.data is incorrectly typed here, so TypeScript does not
22+
// complain if you do the following which throws an error at runtime:
23+
let { title } = rootMatch.data; // 💥
24+
25+
return <html>...</html>;
26+
}
27+
```

packages/react-router/lib/router/utils.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -923,14 +923,23 @@ export interface UIMatch<Data = unknown, Handle = unknown> {
923923
*/
924924
params: AgnosticRouteMatch["params"];
925925
/**
926-
* The return value from the matched route's loader or clientLoader
926+
* The return value from the matched route's loader or clientLoader. This might
927+
* be `undefined` if this route's `loader` (or a deeper route's `loader`) threw
928+
* an error and we're currently displaying an `ErrorBoundary`.
927929
*
928930
* @deprecated Use `UIMatch.loaderData` instead
929931
*/
930-
data: Data;
931-
/** The return value from the matched route's loader or clientLoader */
932-
loaderData: Data;
933-
/** The {@link https://reactrouter.com/start/framework/route-module#handle handle object} exported from the matched route module */
932+
data: Data | undefined;
933+
/**
934+
* The return value from the matched route's loader or clientLoader. This might
935+
* be `undefined` if this route's `loader` (or a deeper route's `loader`) threw
936+
* an error and we're currently displaying an `ErrorBoundary`.
937+
*/
938+
loaderData: Data | undefined;
939+
/**
940+
* The {@link https://reactrouter.com/start/framework/route-module#handle handle object}
941+
* exported from the matched route module
942+
*/
934943
handle: Handle;
935944
}
936945

0 commit comments

Comments
 (0)