-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
refactor: use local matches in adapter Match components #7052
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -287,15 +287,15 @@ export const MatchInner = React.memo(function MatchInnerImpl({ | |
| const out = Comp ? <Comp key={key} /> : <Outlet /> | ||
|
|
||
| if (match._displayPending) { | ||
| throw router.getMatch(match.id)?._nonReactive.displayPendingPromise | ||
| throw match._nonReactive.displayPendingPromise | ||
| } | ||
|
|
||
| if (match._forcePending) { | ||
| throw router.getMatch(match.id)?._nonReactive.minPendingPromise | ||
| throw match._nonReactive.minPendingPromise | ||
| } | ||
|
|
||
| if (match.status === 'pending') { | ||
| throw router.getMatch(match.id)?._nonReactive.loadPromise | ||
| throw match._nonReactive.loadPromise | ||
| } | ||
|
|
||
| if (match.status === 'notFound') { | ||
|
|
@@ -317,7 +317,7 @@ export const MatchInner = React.memo(function MatchInnerImpl({ | |
|
|
||
| invariant() | ||
| } | ||
| throw router.getMatch(match.id)?._nonReactive.loadPromise | ||
| throw match._nonReactive.loadPromise | ||
| } | ||
|
|
||
| if (match.status === 'error') { | ||
|
|
@@ -384,11 +384,11 @@ export const MatchInner = React.memo(function MatchInnerImpl({ | |
| }, [key, route.options.component, router.options.defaultComponent]) | ||
|
|
||
| if (match._displayPending) { | ||
| throw router.getMatch(match.id)?._nonReactive.displayPendingPromise | ||
| throw match._nonReactive.displayPendingPromise | ||
| } | ||
|
|
||
| if (match._forcePending) { | ||
| throw router.getMatch(match.id)?._nonReactive.minPendingPromise | ||
| throw match._nonReactive.minPendingPromise | ||
| } | ||
|
|
||
| // see also hydrate() in packages/router-core/src/ssr/ssr-client.ts | ||
|
|
@@ -397,23 +397,22 @@ export const MatchInner = React.memo(function MatchInnerImpl({ | |
| const pendingMinMs = | ||
| route.options.pendingMinMs ?? router.options.defaultPendingMinMs | ||
| if (pendingMinMs) { | ||
| const routerMatch = router.getMatch(match.id) | ||
| if (routerMatch && !routerMatch._nonReactive.minPendingPromise) { | ||
| if (!match._nonReactive.minPendingPromise) { | ||
| // Create a promise that will resolve after the minPendingMs | ||
| if (!(isServer ?? router.isServer)) { | ||
| const minPendingPromise = createControlledPromise<void>() | ||
|
|
||
| routerMatch._nonReactive.minPendingPromise = minPendingPromise | ||
| match._nonReactive.minPendingPromise = minPendingPromise | ||
|
|
||
| setTimeout(() => { | ||
| minPendingPromise.resolve() | ||
| // We've handled the minPendingPromise, so we can delete it | ||
| routerMatch._nonReactive.minPendingPromise = undefined | ||
| match._nonReactive.minPendingPromise = undefined | ||
| }, pendingMinMs) | ||
| } | ||
| } | ||
| } | ||
| throw router.getMatch(match.id)?._nonReactive.loadPromise | ||
| throw match._nonReactive.loadPromise | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Use of Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| if (match.status === 'notFound') { | ||
|
|
@@ -442,7 +441,7 @@ export const MatchInner = React.memo(function MatchInnerImpl({ | |
| // false, | ||
| // 'Tried to render a redirected route match! This is a weird circumstance, please file an issue!', | ||
| // ) | ||
| throw router.getMatch(match.id)?._nonReactive.loadPromise | ||
| throw match._nonReactive.loadPromise | ||
| } | ||
|
|
||
| if (match.status === 'error') { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -276,9 +276,7 @@ export const MatchInner = (): any => { | |
| <Solid.Match when={currentMatch()._displayPending}> | ||
| {(_) => { | ||
| const [displayPendingResult] = Solid.createResource( | ||
| () => | ||
| router.getMatch(currentMatch().id)?._nonReactive | ||
| .displayPendingPromise, | ||
| () => match()?._nonReactive.displayPendingPromise, | ||
| ) | ||
|
|
||
| return <>{displayPendingResult()}</> | ||
|
|
@@ -287,9 +285,7 @@ export const MatchInner = (): any => { | |
| <Solid.Match when={currentMatch()._forcePending}> | ||
| {(_) => { | ||
| const [minPendingResult] = Solid.createResource( | ||
| () => | ||
| router.getMatch(currentMatch().id)?._nonReactive | ||
| .minPendingPromise, | ||
| () => match()?._nonReactive.minPendingPromise, | ||
| ) | ||
|
|
||
| return <>{minPendingResult()}</> | ||
|
|
@@ -302,31 +298,30 @@ export const MatchInner = (): any => { | |
| router.options.defaultPendingMinMs | ||
|
|
||
| if (pendingMinMs) { | ||
| const routerMatch = router.getMatch(currentMatch().id) | ||
| const matchState = match() | ||
| if ( | ||
| routerMatch && | ||
| !routerMatch._nonReactive.minPendingPromise | ||
| matchState && | ||
| !matchState._nonReactive.minPendingPromise | ||
| ) { | ||
| // Create a promise that will resolve after the minPendingMs | ||
| if (!(isServer ?? router.isServer)) { | ||
| const minPendingPromise = createControlledPromise<void>() | ||
|
|
||
| routerMatch._nonReactive.minPendingPromise = | ||
| matchState._nonReactive.minPendingPromise = | ||
| minPendingPromise | ||
|
|
||
| setTimeout(() => { | ||
| minPendingPromise.resolve() | ||
| // We've handled the minPendingPromise, so we can delete it | ||
| routerMatch._nonReactive.minPendingPromise = undefined | ||
| matchState._nonReactive.minPendingPromise = undefined | ||
|
Comment on lines
+301
to
+316
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clear min-pending state against the live match, not the captured At Lines 313-316, the timer closes over Suggested fix if (!(isServer ?? router.isServer)) {
const minPendingPromise = createControlledPromise<void>()
+ const matchId = matchState.id
matchState._nonReactive.minPendingPromise =
minPendingPromise
setTimeout(() => {
minPendingPromise.resolve()
// We've handled the minPendingPromise, so we can delete it
- matchState._nonReactive.minPendingPromise = undefined
+ router.updateMatch(matchId, (prev) => {
+ if (
+ prev._nonReactive.minPendingPromise ===
+ minPendingPromise
+ ) {
+ prev._nonReactive.minPendingPromise = undefined
+ return { ...prev }
+ }
+ return prev
+ })
}, pendingMinMs)
}🤖 Prompt for AI Agents |
||
| }, pendingMinMs) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| const [loaderResult] = Solid.createResource(async () => { | ||
| await new Promise((r) => setTimeout(r, 0)) | ||
| return router.getMatch(currentMatch().id)?._nonReactive | ||
| .loadPromise | ||
| return match()?._nonReactive.loadPromise | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This resource now reads Useful? React with 👍 / 👎. |
||
| }) | ||
|
|
||
| const FallbackComponent = | ||
|
|
@@ -379,8 +374,7 @@ export const MatchInner = (): any => { | |
|
|
||
| const [loaderResult] = Solid.createResource(async () => { | ||
| await new Promise((r) => setTimeout(r, 0)) | ||
| return router.getMatch(currentMatch().id)?._nonReactive | ||
| .loadPromise | ||
| return match()?._nonReactive.loadPromise | ||
| }) | ||
|
|
||
| return <>{loaderResult()}</> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -378,14 +378,15 @@ export const MatchInner = Vue.defineComponent({ | |
| } | ||
|
|
||
| if (match.value.status === 'redirected') { | ||
| const currentMatch = activeMatch.value | ||
| if (!isRedirect(match.value.error)) { | ||
| if (process.env.NODE_ENV !== 'production') { | ||
| throw new Error('Invariant failed: Expected a redirect error') | ||
| } | ||
|
|
||
| invariant() | ||
| } | ||
| throw router.getMatch(match.value.id)?._nonReactive.loadPromise | ||
| throw currentMatch?._nonReactive.loadPromise | ||
| } | ||
|
|
||
| if (match.value.status === 'error') { | ||
|
|
@@ -414,25 +415,25 @@ export const MatchInner = Vue.defineComponent({ | |
| } | ||
|
|
||
| if (match.value.status === 'pending') { | ||
| const currentMatch = activeMatch.value | ||
| const pendingMinMs = | ||
| route.value.options.pendingMinMs ?? router.options.defaultPendingMinMs | ||
|
|
||
| const routerMatch = router.getMatch(match.value.id) | ||
| if ( | ||
| pendingMinMs && | ||
| routerMatch && | ||
| !routerMatch._nonReactive.minPendingPromise | ||
| currentMatch && | ||
| !currentMatch._nonReactive.minPendingPromise | ||
| ) { | ||
| // Create a promise that will resolve after the minPendingMs | ||
| if (!(isServer ?? router.isServer)) { | ||
| const minPendingPromise = createControlledPromise<void>() | ||
|
|
||
| routerMatch._nonReactive.minPendingPromise = minPendingPromise | ||
| currentMatch._nonReactive.minPendingPromise = minPendingPromise | ||
|
|
||
| setTimeout(() => { | ||
| minPendingPromise.resolve() | ||
| // We've handled the minPendingPromise, so we can delete it | ||
| routerMatch._nonReactive.minPendingPromise = undefined | ||
| currentMatch._nonReactive.minPendingPromise = undefined | ||
| }, pendingMinMs) | ||
|
Comment on lines
422
to
437
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid clearing min-pending state on the captured At Lines 433-436, the timeout mutates Suggested fix if (
pendingMinMs &&
currentMatch &&
!currentMatch._nonReactive.minPendingPromise
) {
+ const matchId = currentMatch.id
// Create a promise that will resolve after the minPendingMs
if (!(isServer ?? router.isServer)) {
const minPendingPromise = createControlledPromise<void>()
currentMatch._nonReactive.minPendingPromise = minPendingPromise
setTimeout(() => {
minPendingPromise.resolve()
// We've handled the minPendingPromise, so we can delete it
- currentMatch._nonReactive.minPendingPromise = undefined
+ router.updateMatch(matchId, (prev) => {
+ if (prev._nonReactive.minPendingPromise === minPendingPromise) {
+ prev._nonReactive.minPendingPromise = undefined
+ return { ...prev }
+ }
+ return prev
+ })
}, pendingMinMs)
}
}🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clear
minPendingPromisethrough the store in the timeout.At Lines 407-410, the callback mutates the closed-over
matchobject. Match stores replace their state object on updates, so if this match is replaced before the timer fires, the live store keeps stale min-pending state and can remain forced-pending. Please clear it throughmatchStore.setState(...)orrouter.updateMatch(...)instead of mutating the captured object. The analogous SSR hydrate path already does this.Suggested fix
setTimeout(() => { minPendingPromise.resolve() // We've handled the minPendingPromise, so we can delete it - match._nonReactive.minPendingPromise = undefined + matchStore.setState((prev) => { + if (prev._nonReactive.minPendingPromise === minPendingPromise) { + prev._nonReactive.minPendingPromise = undefined + return { ...prev } + } + return prev + }) }, pendingMinMs)🤖 Prompt for AI Agents