@@ -1481,6 +1481,7 @@ export function createRouter(init: RouterInit): Router {
1481
1481
cancelledDeferredRoutes ,
1482
1482
cancelledFetcherLoads ,
1483
1483
fetchLoadMatches ,
1484
+ fetchRedirectIds ,
1484
1485
routesToUse ,
1485
1486
basename ,
1486
1487
pendingActionData ,
@@ -1539,6 +1540,9 @@ export function createRouter(init: RouterInit): Router {
1539
1540
1540
1541
pendingNavigationLoadId = ++ incrementingLoadId ;
1541
1542
revalidatingFetchers . forEach ( ( rf ) => {
1543
+ if ( fetchControllers . has ( rf . key ) ) {
1544
+ abortFetcher ( rf . key ) ;
1545
+ }
1542
1546
if ( rf . controller ) {
1543
1547
// Fetchers use an independent AbortController so that aborting a fetcher
1544
1548
// (via deleteFetcher) does not abort the triggering navigation that
@@ -1806,6 +1810,7 @@ export function createRouter(init: RouterInit): Router {
1806
1810
cancelledDeferredRoutes ,
1807
1811
cancelledFetcherLoads ,
1808
1812
fetchLoadMatches ,
1813
+ fetchRedirectIds ,
1809
1814
routesToUse ,
1810
1815
basename ,
1811
1816
{ [ match . route . id ] : actionResult . data } ,
@@ -1825,6 +1830,9 @@ export function createRouter(init: RouterInit): Router {
1825
1830
existingFetcher ? existingFetcher . data : undefined
1826
1831
) ;
1827
1832
state . fetchers . set ( staleKey , revalidatingFetcher ) ;
1833
+ if ( fetchControllers . has ( staleKey ) ) {
1834
+ abortFetcher ( staleKey ) ;
1835
+ }
1828
1836
if ( rf . controller ) {
1829
1837
fetchControllers . set ( staleKey , rf . controller ) ;
1830
1838
}
@@ -3276,6 +3284,7 @@ function getMatchesToLoad(
3276
3284
cancelledDeferredRoutes : string [ ] ,
3277
3285
cancelledFetcherLoads : string [ ] ,
3278
3286
fetchLoadMatches : Map < string , FetchLoadMatch > ,
3287
+ fetchRedirectIds : Set < string > ,
3279
3288
routesToUse : AgnosticDataRouteObject [ ] ,
3280
3289
basename : string | undefined ,
3281
3290
pendingActionData ?: RouteData ,
@@ -3361,34 +3370,38 @@ function getMatchesToLoad(
3361
3370
return ;
3362
3371
}
3363
3372
3373
+ // Revalidating fetchers are decoupled from the route matches since they
3374
+ // load from a static href. They only set `defaultShouldRevalidate` on
3375
+ // explicit revalidation due to submission, useRevalidator, or X-Remix-Revalidate
3376
+ //
3377
+ // They automatically revalidate without even calling shouldRevalidate if:
3378
+ // - They were cancelled
3379
+ // - They're in the middle of their first load and therefore this is still
3380
+ // an initial load and not a revalidation
3381
+ //
3382
+ // If neither of those is true, then they _always_ check shouldRevalidate
3383
+ let fetcher = state . fetchers . get ( key ) ;
3384
+ let isPerformingInitialLoad =
3385
+ fetcher &&
3386
+ fetcher . state !== "idle" &&
3387
+ fetcher . data === undefined &&
3388
+ // If a fetcher.load redirected then it'll be "loading" without any data
3389
+ // so ensure we're not processing the redirect from this fetcher
3390
+ ! fetchRedirectIds . has ( key ) ;
3364
3391
let fetcherMatch = getTargetMatch ( fetcherMatches , f . path ) ;
3365
-
3366
- if ( cancelledFetcherLoads . includes ( key ) ) {
3367
- revalidatingFetchers . push ( {
3368
- key,
3369
- routeId : f . routeId ,
3370
- path : f . path ,
3371
- matches : fetcherMatches ,
3372
- match : fetcherMatch ,
3373
- controller : new AbortController ( ) ,
3392
+ let shouldRevalidate =
3393
+ cancelledFetcherLoads . includes ( key ) ||
3394
+ isPerformingInitialLoad ||
3395
+ shouldRevalidateLoader ( fetcherMatch , {
3396
+ currentUrl,
3397
+ currentParams : state . matches [ state . matches . length - 1 ] . params ,
3398
+ nextUrl,
3399
+ nextParams : matches [ matches . length - 1 ] . params ,
3400
+ ...submission ,
3401
+ actionResult,
3402
+ defaultShouldRevalidate : isRevalidationRequired ,
3374
3403
} ) ;
3375
- return ;
3376
- }
3377
3404
3378
- // Revalidating fetchers are decoupled from the route matches since they
3379
- // hit a static href, so they _always_ check shouldRevalidate and the
3380
- // default is strictly if a revalidation is explicitly required (action
3381
- // submissions, useRevalidator, X-Remix-Revalidate).
3382
- let shouldRevalidate = shouldRevalidateLoader ( fetcherMatch , {
3383
- currentUrl,
3384
- currentParams : state . matches [ state . matches . length - 1 ] . params ,
3385
- nextUrl,
3386
- nextParams : matches [ matches . length - 1 ] . params ,
3387
- ...submission ,
3388
- actionResult,
3389
- // Forced revalidation due to submission, useRevalidator, or X-Remix-Revalidate
3390
- defaultShouldRevalidate : isRevalidationRequired ,
3391
- } ) ;
3392
3405
if ( shouldRevalidate ) {
3393
3406
revalidatingFetchers . push ( {
3394
3407
key,
0 commit comments