@@ -1363,6 +1363,19 @@ export function createRouter(init: RouterInit): Router {
13631363 )
13641364 : state . loaderData ;
13651365
1366+ // Transition any fetchers that were kept in loading state (with formData) to idle
1367+ // now that we're committing the loaderData. This ensures the fetcher state change
1368+ // and loaderData update happen atomically in the same updateState() call.
1369+ let fetchers = newState . fetchers ? new Map ( newState . fetchers ) : new Map ( state . fetchers ) ;
1370+ let updatedFetchers = false ;
1371+ fetchers . forEach ( ( fetcher , key ) => {
1372+ if ( fetcher . state === "loading" && fetcher . formData ) {
1373+ // Transition to idle now that loaderData is being committed
1374+ fetchers . set ( key , getDoneFetcher ( fetcher . data ) ) ;
1375+ updatedFetchers = true ;
1376+ }
1377+ } ) ;
1378+
13661379 // On a successful navigation we can assume we got through all blockers
13671380 // so we can start fresh
13681381 let blockers = state . blockers ;
@@ -1436,7 +1449,7 @@ export function createRouter(init: RouterInit): Router {
14361449
14371450 updateState (
14381451 {
1439- ...newState , // matches, errors, fetchers go through as-is
1452+ ...newState , // matches, errors go through as-is
14401453 actionData,
14411454 loaderData,
14421455 historyAction : pendingAction ,
@@ -1447,6 +1460,8 @@ export function createRouter(init: RouterInit): Router {
14471460 restoreScrollPosition,
14481461 preventScrollReset,
14491462 blockers,
1463+ // Use updated fetchers if we transitioned any from loading to idle
1464+ ...( updatedFetchers ? { fetchers } : { } ) ,
14501465 } ,
14511466 {
14521467 viewTransitionOpts,
@@ -6549,8 +6564,22 @@ function processLoaderData(
65496564 // keep this to type narrow to a success result in the else
65506565 invariant ( false , "Unhandled fetcher revalidation redirect" ) ;
65516566 } else {
6567+ // Get the current fetcher state to check if it has formData
6568+ let existingFetcher = state . fetchers . get ( key ) ;
65526569 let doneFetcher = getDoneFetcher ( result . data ) ;
6553- state . fetchers . set ( key , doneFetcher ) ;
6570+
6571+ // If the fetcher currently has formData, keep it
6572+ // in loading state with the new data until completeNavigation commits both
6573+ // the fetcher state and loaderData together. This prevents a flicker where
6574+ // fetcher.formData becomes undefined before new loaderData is available.
6575+ if ( existingFetcher && existingFetcher . formData ) {
6576+ state . fetchers . set ( key , {
6577+ ...existingFetcher ,
6578+ data : result . data ,
6579+ } ) ;
6580+ } else {
6581+ state . fetchers . set ( key , doneFetcher ) ;
6582+ }
65546583 }
65556584 } ) ;
65566585
0 commit comments