Skip to content

Commit 6413001

Browse files
committed
Use granular route.lazy for loaders/actions
1 parent d07cefe commit 6413001

File tree

2 files changed

+54
-34
lines changed

2 files changed

+54
-34
lines changed

packages/react-router/__tests__/router/lazy-test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2305,7 +2305,7 @@ describe("lazily loaded route modules", () => {
23052305
it("handles errors when failing to resolve lazy route properties on submission navigation", async () => {
23062306
let { lazyStub, lazyDeferred } = createLazyStub();
23072307
let routes = createBasicLazyRoutes({
2308-
loader: lazyStub,
2308+
action: lazyStub,
23092309
});
23102310
let t = setup({ routes });
23112311
expect(lazyStub).not.toHaveBeenCalled();
@@ -2627,7 +2627,7 @@ describe("lazily loaded route modules", () => {
26272627
it("handles errors when failing to load lazy route properties on fetcher.submit", async () => {
26282628
let { lazyStub, lazyDeferred } = createLazyStub();
26292629
let routes = createBasicLazyRoutes({
2630-
loader: lazyStub,
2630+
action: lazyStub,
26312631
});
26322632
let t = setup({ routes });
26332633
expect(lazyStub).not.toHaveBeenCalled();

packages/react-router/lib/router/router.ts

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4940,32 +4940,41 @@ const lazyRouteFunctionCache = new WeakMap<
49404940
* shouldRevalidate) and update the routeManifest in place which shares objects
49414941
* with dataRoutes so those get updated as well.
49424942
*/
4943-
async function loadLazyRoute(
4943+
function loadLazyRoute(
49444944
route: AgnosticDataRouteObject,
49454945
manifest: RouteManifest,
49464946
mapRouteProperties: MapRoutePropertiesFunction
4947-
) {
4947+
): {
4948+
routePromise: Promise<void> | undefined;
4949+
propertyPromises?: Partial<
4950+
Record<keyof AgnosticDataRouteObject, Promise<void>>
4951+
>;
4952+
} {
49484953
let routeToUpdate = manifest[route.id];
49494954
invariant(routeToUpdate, "No route found in manifest");
49504955

49514956
if (!route.lazy) {
4952-
return;
4957+
return { routePromise: undefined };
49534958
}
49544959

49554960
if (typeof route.lazy === "function") {
49564961
// Check if we have a cached promise from a previous call
49574962
let cachedPromise = lazyRouteFunctionCache.get(routeToUpdate);
49584963
if (cachedPromise) {
4959-
await cachedPromise;
4960-
return;
4964+
return { routePromise: cachedPromise };
49614965
}
49624966

49634967
// We use `.then` to chain additional logic to the lazy route promise so that
49644968
// the consumer's lazy route logic is coupled to our logic for updating the
49654969
// route in place in a single task. This ensures that the cached promise
49664970
// contains all logic for managing the lazy route. This chained promise is
49674971
// then awaited so that consumers of this function see the updated route.
4968-
let lazyRoutePromise = route.lazy().then((lazyRoute) => {
4972+
let lazyRoutePromise = (async () => {
4973+
invariant(
4974+
typeof route.lazy === "function",
4975+
"No lazy route function found"
4976+
);
4977+
let lazyRoute = await route.lazy();
49694978
// Here we update the route in place. This should be safe because there's
49704979
// no way we could yet be sitting on this route as we can't get there
49714980
// without resolving lazy() first.
@@ -5025,26 +5034,32 @@ async function loadLazyRoute(
50255034
...mapRouteProperties(routeToUpdate),
50265035
lazy: undefined,
50275036
});
5028-
});
5037+
})();
50295038

50305039
lazyRouteFunctionCache.set(routeToUpdate, lazyRoutePromise);
5031-
await lazyRoutePromise;
5032-
5033-
return;
5040+
return { routePromise: lazyRoutePromise };
50345041
}
50355042

5036-
let lazyKeys = Object.keys(route.lazy) as Array<keyof typeof route.lazy>;
5043+
type LazyKey = keyof typeof route.lazy;
5044+
let lazyKeys = Object.keys(route.lazy) as Array<LazyKey>;
5045+
let propertyPromises: Partial<Record<LazyKey, Promise<void>>> = {};
5046+
for (let key of lazyKeys) {
5047+
let promise = loadLazyRouteProperty({
5048+
key,
5049+
route,
5050+
manifest,
5051+
mapRouteProperties,
5052+
});
5053+
if (promise) {
5054+
propertyPromises[key] = promise;
5055+
}
5056+
}
50375057

5038-
await Promise.all(
5039-
lazyKeys.map((key) =>
5040-
loadLazyRouteProperty({
5041-
key,
5042-
route,
5043-
manifest,
5044-
mapRouteProperties,
5045-
})
5046-
)
5058+
let routePromise = Promise.all(Object.values(propertyPromises)).then(
5059+
() => {}
50475060
);
5061+
5062+
return { routePromise, propertyPromises };
50485063
}
50495064

50505065
function loadLazyMiddlewareForMatches(
@@ -5264,10 +5279,8 @@ async function callDataStrategyImpl(
52645279
manifest,
52655280
mapRouteProperties
52665281
);
5267-
let loadLazyRoutePromises = matches.map((m) =>
5268-
m.route.lazy
5269-
? loadLazyRoute(m.route, manifest, mapRouteProperties)
5270-
: undefined
5282+
let loadLazyRouteResults = matches.map((m) =>
5283+
loadLazyRoute(m.route, manifest, mapRouteProperties)
52715284
);
52725285

52735286
// Ensure all middleware is loaded before we start executing routes
@@ -5276,7 +5289,7 @@ async function callDataStrategyImpl(
52765289
}
52775290

52785291
let dsMatches = matches.map((match, i) => {
5279-
let loadRoutePromise = loadLazyRoutePromises[i];
5292+
let { routePromise, propertyPromises } = loadLazyRouteResults[i];
52805293
let shouldLoad = matchesToLoad.some((m) => m.route.id === match.route.id);
52815294
// `resolve` encapsulates route.lazy(), executing the loader/action,
52825295
// and mapping return values/thrown errors to a `DataStrategyResult`. Users
@@ -5290,12 +5303,16 @@ async function callDataStrategyImpl(
52905303
) {
52915304
shouldLoad = true;
52925305
}
5306+
let handlerPromise = propertyPromises
5307+
? propertyPromises[type]
5308+
: routePromise;
52935309
return shouldLoad
52945310
? callLoaderOrAction(
52955311
type,
52965312
request,
52975313
match,
5298-
loadRoutePromise,
5314+
handlerPromise,
5315+
routePromise,
52995316
handlerOverride,
53005317
scopedContext
53015318
)
@@ -5324,7 +5341,9 @@ async function callDataStrategyImpl(
53245341
// it to bubble up from the `await loadRoutePromise` in `callLoaderOrAction` -
53255342
// called from `match.resolve()`
53265343
try {
5327-
await Promise.all(loadLazyRoutePromises);
5344+
await Promise.all(
5345+
loadLazyRouteResults.map(({ routePromise }) => routePromise)
5346+
);
53285347
} catch (e) {
53295348
// No-op
53305349
}
@@ -5337,6 +5356,7 @@ async function callLoaderOrAction(
53375356
type: "loader" | "action",
53385357
request: Request,
53395358
match: AgnosticDataRouteMatch,
5359+
loadHandlerPromise: Promise<void> | undefined,
53405360
loadRoutePromise: Promise<void> | undefined,
53415361
handlerOverride: Parameters<DataStrategyMatch["resolve"]>[0],
53425362
scopedContext: unknown
@@ -5394,7 +5414,7 @@ async function callLoaderOrAction(
53945414
| ActionFunction<unknown>;
53955415

53965416
// If we have a route.lazy promise, await that first
5397-
if (loadRoutePromise) {
5417+
if (loadHandlerPromise) {
53985418
if (handler) {
53995419
// Run statically defined handler in parallel with lazy()
54005420
let handlerError;
@@ -5405,15 +5425,15 @@ async function callLoaderOrAction(
54055425
runHandler(handler).catch((e) => {
54065426
handlerError = e;
54075427
}),
5408-
loadRoutePromise,
5428+
loadHandlerPromise,
54095429
]);
54105430
if (handlerError !== undefined) {
54115431
throw handlerError;
54125432
}
54135433
result = value!;
54145434
} else {
5415-
// Load lazy route module, then run any returned handler
5416-
await loadRoutePromise;
5435+
// Load lazy loader/action, then run any returned handler
5436+
await loadHandlerPromise;
54175437

54185438
handler = match.route[type] as
54195439
| LoaderFunction<unknown>
@@ -5422,7 +5442,7 @@ async function callLoaderOrAction(
54225442
// Handler still runs even if we got interrupted to maintain consistency
54235443
// with un-abortable behavior of handler execution on non-lazy or
54245444
// previously-lazy-loaded routes
5425-
result = await runHandler(handler);
5445+
[result] = await Promise.all([runHandler(handler), loadRoutePromise]);
54265446
} else if (type === "action") {
54275447
let url = new URL(request.url);
54285448
let pathname = url.pathname + url.search;

0 commit comments

Comments
 (0)