diff --git a/packages/router-core/src/load-client.ts b/packages/router-core/src/load-client.ts index f0024720b8..7425cce078 100644 --- a/packages/router-core/src/load-client.ts +++ b/packages/router-core/src/load-client.ts @@ -677,18 +677,19 @@ function settleInto( result: LoaderOutcome, preload: boolean, ): asserts match is SettledMatch { + if (result[0 /* kind */] === REDIRECTED) { + return + } + // Reduction installs only the selected terminal failure. Every other + // settled attempt remains a renderable, stale match in that lane. + match.status = 'success' + match.error = undefined if (result[0 /* kind */] === SUCCESS) { match.loaderData = result[1 /* data */] - match.error = undefined - match.status = 'success' match.invalid = false match.updatedAt = Date.now() match.preload = preload - } else if (result[0 /* kind */] !== REDIRECTED) { - // Reduction installs only the selected terminal failure. Every other - // settled attempt remains a renderable, stale match in that lane. - match.status = 'success' - match.error = undefined + } else { match.invalid = true } } @@ -780,7 +781,7 @@ function createLoaderTask( reload = true } else { const staleAge = - options[4 /* preload */] || match.preload + preload || match.preload ? (route.options.preloadStaleTime ?? router.options.defaultPreloadStaleTime ?? 30_000) @@ -806,10 +807,11 @@ function createLoaderTask( reloadFailure = normalizeLaneError(route, cause, options) } const routeLoader = route.options.loader - const loader = - typeof routeLoader === 'function' ? routeLoader : routeLoader?.handler + const isLoaderFn = typeof routeLoader === 'function' + const loader = isLoaderFn ? routeLoader : routeLoader?.handler + const preloadable = !preload || route.options.preload !== false let donor = - (!preload || route.options.preload !== false) && + preloadable && routeLoader && !(process.env.NODE_ENV !== 'production' && router._tx?.[6 /* refresh */]) ? router._flights?.get(match.id) @@ -829,12 +831,10 @@ function createLoaderTask( match.status === 'success' && !preload && !options[5 /* sync */] && - ((typeof routeLoader === 'function' - ? undefined - : routeLoader?.staleReloadMode) ?? + ((isLoaderFn ? undefined : routeLoader.staleReloadMode) ?? router.options.defaultStaleReloadMode) !== 'blocking' ) - const loaded = reload && (!preload || route.options.preload !== false) + const loaded = reload && preloadable const blocking = loaded && !background && (match.status !== 'success' || !!routeLoader) const onLazyReady = @@ -1248,10 +1248,9 @@ async function executeClientLane( options[0 /* controller */].signal, plannedBoundary, ) - if (boundary !== plannedBoundary) { - matches[plannedBoundary]!._notFound = undefined - matches[boundary]!._notFound = true - } + // When the boundary did not move this clears and re-sets the same match. + matches[plannedBoundary]!._notFound = undefined + matches[boundary]!._notFound = true plannedBoundary = boundary } let end = plannedBoundary < 0 ? matches.length : plannedBoundary + 1 @@ -1419,8 +1418,10 @@ function offerPending(router: CoordinatorRouter, tx: LoadTransaction): void { continue } const route = getRoute(router, match as WorkMatch) + // The continue above already filtered non-presented successes, so a + // success here is a presented pending fallback and reveals immediately. delay = - (success && presentedPending) || match.invalid + success || match.invalid ? 0 : (route.options.pendingMs ?? router.options.defaultPendingMs) component = @@ -1515,6 +1516,12 @@ function publishMatches( router.stores.setMatches(matches) } +/** Release a `commit()` awaiter once its transaction reaches a settled UI. */ +function resolveCommit(router: CoordinatorRouter): void { + router._commitPromise?.resolve() + router._commitPromise = undefined +} + function discardLane(router: AnyRouter, lane: ProjectedLane): void { transferMatchResources(router, lane[1 /* matches */]) discardBackground(router, lane) @@ -1673,8 +1680,7 @@ function rollbackPublication( transferMatchResources(router, discarded, restored) discardBackground(router, lane) if (router._tx === tx && router._commitPromise === checkpoint.commitPromise) { - router._commitPromise?.resolve() - router._commitPromise = undefined + resolveCommit(router) } return true } @@ -1780,8 +1786,7 @@ function restoreCommitted( router.stores.setMatches(router._committed) }) if (router._tx === tx) { - router._commitPromise?.resolve() - router._commitPromise = undefined + resolveCommit(router) } } @@ -1994,8 +1999,7 @@ async function runClientTransaction( if (router._tx !== tx) { return } - router._commitPromise?.resolve() - router._commitPromise = undefined + resolveCommit(router) }) } @@ -2068,8 +2072,7 @@ export async function loadClientRoute( router._refreshNextLoad = undefined } await awaitCurrent(router) - router._commitPromise?.resolve() - router._commitPromise = undefined + resolveCommit(router) return } await router.navigate({