Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/react-router/src/CatchBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,18 @@ class CatchBoundaryImpl extends React.Component<{
return { resetKey }
}
static getDerivedStateFromError(error: Error) {
return { error }
return { error: error ?? new Error('A nullish value was thrown') }
}
reset() {
this.setState({ error: null })
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
if (error == null && this.state.error) {
this.state.error.stack =
errorInfo.componentStack ?? this.state.error.stack
}
if (this.props.onCatch) {
this.props.onCatch(error, errorInfo)
this.props.onCatch(this.state.error ?? error, errorInfo)
}
}
render() {
Expand Down
22 changes: 13 additions & 9 deletions packages/react-router/src/Match.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,8 @@ function OnRendered() {
return null
}

const permaPendingPromise = new Promise(() => {})

export const MatchInner = React.memo(function MatchInnerImpl({
matchId,
}: {
Expand Down Expand Up @@ -338,15 +340,17 @@ export const MatchInner = React.memo(function MatchInnerImpl({
const out = Comp ? <Comp key={key} /> : <Outlet />

if (match._displayPending) {
throw getMatchPromise(match, 'displayPendingPromise')
throw (
getMatchPromise(match, 'displayPendingPromise') ?? permaPendingPromise
)
}

if (match._forcePending) {
throw getMatchPromise(match, 'minPendingPromise')
throw getMatchPromise(match, 'minPendingPromise') ?? permaPendingPromise
}

if (match.status === 'pending') {
throw getMatchPromise(match, 'loadPromise')
throw getMatchPromise(match, 'loadPromise') ?? permaPendingPromise
}

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

invariant()
}
throw getMatchPromise(match, 'loadPromise')
throw getMatchPromise(match, 'loadPromise') ?? permaPendingPromise
}

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

if (match._displayPending) {
throw getMatchPromise(match, 'displayPendingPromise')
throw getMatchPromise(match, 'displayPendingPromise') ?? permaPendingPromise
}

if (match._forcePending) {
throw getMatchPromise(match, 'minPendingPromise')
throw getMatchPromise(match, 'minPendingPromise') ?? permaPendingPromise
}

// see also hydrate() in packages/router-core/src/ssr/ssr-client.ts
Expand All @@ -464,7 +468,7 @@ export const MatchInner = React.memo(function MatchInnerImpl({
}
}
}
throw getMatchPromise(match, 'loadPromise')
throw getMatchPromise(match, 'loadPromise') ?? permaPendingPromise
}

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

throw getMatchPromise(match, 'loadPromise')
throw getMatchPromise(match, 'loadPromise') ?? permaPendingPromise
}

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

throw match.error
throw match.error ?? new Error('Unknown match error!')
}

return out
Expand Down