Skip to content

Commit 61fa627

Browse files
committed
fix(react-router): Catch thrown nullish value and prevent nullish
loadPromise from being thrown The issue happens in circumstances where match.status === 'redirected' but the loadPromise is nulled. Reproduction is possible by using https://github.com/joseinaqa/redirect-race-app and after this change does not anymore.
1 parent ecbbd9a commit 61fa627

2 files changed

Lines changed: 19 additions & 11 deletions

File tree

packages/react-router/src/CatchBoundary.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,18 @@ class CatchBoundaryImpl extends React.Component<{
5353
return { resetKey }
5454
}
5555
static getDerivedStateFromError(error: Error) {
56-
return { error }
56+
return { error: error ?? new Error('A nullish value was thrown') }
5757
}
5858
reset() {
5959
this.setState({ error: null })
6060
}
6161
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
62+
if (error == null && this.state.error) {
63+
this.state.error.stack =
64+
errorInfo.componentStack ?? this.state.error.stack
65+
}
6266
if (this.props.onCatch) {
63-
this.props.onCatch(error, errorInfo)
67+
this.props.onCatch(this.state.error ?? error, errorInfo)
6468
}
6569
}
6670
render() {

packages/react-router/src/Match.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,8 @@ function OnRendered() {
287287
return null
288288
}
289289

290+
const permaPendingPromise = new Promise(() => {})
291+
290292
export const MatchInner = React.memo(function MatchInnerImpl({
291293
matchId,
292294
}: {
@@ -338,15 +340,17 @@ export const MatchInner = React.memo(function MatchInnerImpl({
338340
const out = Comp ? <Comp key={key} /> : <Outlet />
339341

340342
if (match._displayPending) {
341-
throw getMatchPromise(match, 'displayPendingPromise')
343+
throw (
344+
getMatchPromise(match, 'displayPendingPromise') ?? permaPendingPromise
345+
)
342346
}
343347

344348
if (match._forcePending) {
345-
throw getMatchPromise(match, 'minPendingPromise')
349+
throw getMatchPromise(match, 'minPendingPromise') ?? permaPendingPromise
346350
}
347351

348352
if (match.status === 'pending') {
349-
throw getMatchPromise(match, 'loadPromise')
353+
throw getMatchPromise(match, 'loadPromise') ?? permaPendingPromise
350354
}
351355

352356
if (match.status === 'notFound') {
@@ -368,7 +372,7 @@ export const MatchInner = React.memo(function MatchInnerImpl({
368372

369373
invariant()
370374
}
371-
throw getMatchPromise(match, 'loadPromise')
375+
throw getMatchPromise(match, 'loadPromise') ?? permaPendingPromise
372376
}
373377

374378
if (match.status === 'error') {
@@ -435,11 +439,11 @@ export const MatchInner = React.memo(function MatchInnerImpl({
435439
}, [key, route.options.component, router.options.defaultComponent])
436440

437441
if (match._displayPending) {
438-
throw getMatchPromise(match, 'displayPendingPromise')
442+
throw getMatchPromise(match, 'displayPendingPromise') ?? permaPendingPromise
439443
}
440444

441445
if (match._forcePending) {
442-
throw getMatchPromise(match, 'minPendingPromise')
446+
throw getMatchPromise(match, 'minPendingPromise') ?? permaPendingPromise
443447
}
444448

445449
// see also hydrate() in packages/router-core/src/ssr/ssr-client.ts
@@ -464,7 +468,7 @@ export const MatchInner = React.memo(function MatchInnerImpl({
464468
}
465469
}
466470
}
467-
throw getMatchPromise(match, 'loadPromise')
471+
throw getMatchPromise(match, 'loadPromise') ?? permaPendingPromise
468472
}
469473

470474
if (match.status === 'notFound') {
@@ -491,7 +495,7 @@ export const MatchInner = React.memo(function MatchInnerImpl({
491495
invariant()
492496
}
493497

494-
throw getMatchPromise(match, 'loadPromise')
498+
throw getMatchPromise(match, 'loadPromise') ?? permaPendingPromise
495499
}
496500

497501
if (match.status === 'error') {
@@ -516,7 +520,7 @@ export const MatchInner = React.memo(function MatchInnerImpl({
516520
)
517521
}
518522

519-
throw match.error
523+
throw match.error ?? new Error('Unknown match error!')
520524
}
521525

522526
return out

0 commit comments

Comments
 (0)