Skip to content

Commit 57873d1

Browse files
Use granular route.lazy for loaders/actions (#13339)
1 parent 9a7390b commit 57873d1

File tree

3 files changed

+197
-79
lines changed

3 files changed

+197
-79
lines changed

.changeset/tidy-parrots-rush.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
[REMOVE]: Additional work on route.lazy object form

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

Lines changed: 93 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,16 @@ describe("lazily loaded route modules", () => {
109109

110110
router.initialize();
111111

112-
let loader = () => null;
112+
let loader = jest.fn(() => null);
113113
await lazyLoaderDeferred.resolve(loader);
114114

115-
let action = () => null;
115+
// Ensure loader is called as soon as it's loaded
116+
expect(loader).toHaveBeenCalledTimes(1);
117+
118+
// Finish loading all lazy properties
119+
let action = jest.fn(() => null);
116120
await lazyActionDeferred.resolve(action);
121+
expect(action).toHaveBeenCalledTimes(0);
117122

118123
expect(router.state.location.pathname).toBe("/lazy");
119124
expect(router.state.navigation.state).toBe("idle");
@@ -222,15 +227,10 @@ describe("lazily loaded route modules", () => {
222227
lazy: async () => {
223228
throw new Error("SHOULD NOT BE CALLED");
224229
},
225-
// @ts-expect-error
226230
caseSensitive: async () => true,
227-
// @ts-expect-error
228231
path: async () => "/lazy/path",
229-
// @ts-expect-error
230232
id: async () => "lazy",
231-
// @ts-expect-error
232233
index: async () => true,
233-
// @ts-expect-error
234234
children: async () => [],
235235
},
236236
},
@@ -304,12 +304,14 @@ describe("lazily loaded route modules", () => {
304304

305305
it("resolves lazy route properties and executes loaders on router initialization", async () => {
306306
let lazyLoaderDeferred = createDeferred();
307+
let lazyActionDeferred = createDeferred();
307308
let router = createRouter({
308309
routes: [
309310
{
310311
path: "/lazy",
311312
lazy: {
312313
loader: () => lazyLoaderDeferred.promise,
314+
action: () => lazyActionDeferred.promise,
313315
},
314316
},
315317
],
@@ -320,11 +322,17 @@ describe("lazily loaded route modules", () => {
320322

321323
router.initialize();
322324

323-
let loaderDeferred = createDeferred();
324-
let loader = () => loaderDeferred.promise;
325+
// Ensure loader is called as soon as it's loaded
326+
let { lazyStub: loader, lazyDeferred: loaderDeferred } = createLazyStub();
325327
await lazyLoaderDeferred.resolve(loader);
328+
expect(loader).toHaveBeenCalledTimes(1);
326329
expect(router.state.initialized).toBe(false);
327330

331+
// Finish loading all lazy properties
332+
let action = jest.fn(() => null);
333+
await lazyActionDeferred.resolve(action);
334+
expect(action).toHaveBeenCalledTimes(0);
335+
328336
await loaderDeferred.resolve("LOADER");
329337
expect(router.state.location.pathname).toBe("/lazy");
330338
expect(router.state.navigation.state).toBe("idle");
@@ -366,32 +374,46 @@ describe("lazily loaded route modules", () => {
366374
});
367375

368376
it("resolves lazy route properties on loading navigation", async () => {
369-
let { lazyStub, lazyDeferred } = createLazyStub();
377+
let { lazyStub: lazyLoader, lazyDeferred: lazyLoaderDeferred } =
378+
createLazyStub();
379+
let { lazyStub: lazyAction, lazyDeferred: lazyActionDeferred } =
380+
createLazyStub();
370381
let routes = createBasicLazyRoutes({
371-
loader: lazyStub,
382+
loader: lazyLoader,
383+
action: lazyAction,
372384
});
373385
let t = setup({ routes });
374-
expect(lazyStub).not.toHaveBeenCalled();
386+
expect(lazyLoader).not.toHaveBeenCalled();
375387

376388
await t.navigate("/lazy");
377389
expect(t.router.state.location.pathname).toBe("/");
378390
expect(t.router.state.navigation.state).toBe("loading");
379-
expect(lazyStub).toHaveBeenCalledTimes(1);
391+
expect(lazyLoader).toHaveBeenCalledTimes(1);
380392

381-
let loaderDeferred = createDeferred();
382-
lazyDeferred.resolve(() => loaderDeferred.promise);
393+
// Ensure loader is called as soon as it's loaded
394+
let { lazyStub: loader, lazyDeferred: loaderDeferred } = createLazyStub();
395+
await lazyLoaderDeferred.resolve(loader);
396+
expect(loader).toHaveBeenCalledTimes(1);
383397
expect(t.router.state.location.pathname).toBe("/");
384398
expect(t.router.state.navigation.state).toBe("loading");
385-
expect(lazyStub).toHaveBeenCalledTimes(1);
386399

400+
// Ensure we're still loading if other lazy properties are not loaded yet
387401
await loaderDeferred.resolve("LAZY LOADER");
402+
expect(t.router.state.location.pathname).toBe("/");
403+
expect(t.router.state.navigation.state).toBe("loading");
404+
405+
// Finish loading all lazy properties
406+
let action = jest.fn(() => null);
407+
await lazyActionDeferred.resolve(action);
408+
expect(action).toHaveBeenCalledTimes(0);
388409

389410
expect(t.router.state.location.pathname).toBe("/lazy");
390411
expect(t.router.state.navigation.state).toBe("idle");
391412
expect(t.router.state.loaderData).toEqual({
392413
lazy: "LAZY LOADER",
393414
});
394-
expect(lazyStub).toHaveBeenCalledTimes(1);
415+
expect(lazyLoader).toHaveBeenCalledTimes(1);
416+
expect(lazyAction).toHaveBeenCalledTimes(1);
395417
});
396418

397419
it("ignores falsy lazy route properties on loading navigation", async () => {
@@ -480,21 +502,25 @@ describe("lazily loaded route modules", () => {
480502
expect(lazyLoaderStub).toHaveBeenCalledTimes(1);
481503
expect(lazyActionStub).toHaveBeenCalledTimes(1);
482504

483-
let actionDeferred = createDeferred();
484-
let loaderDeferred = createDeferred();
485-
lazyLoaderDeferred.resolve(() => loaderDeferred.promise);
486-
lazyActionDeferred.resolve(() => actionDeferred.promise);
487-
expect(t.router.state.location.pathname).toBe("/");
488-
expect(t.router.state.navigation.state).toBe("submitting");
505+
let { lazyStub: action, lazyDeferred: actionDeferred } = createLazyStub();
506+
let { lazyStub: loader, lazyDeferred: loaderDeferred } = createLazyStub();
489507

508+
// Ensure action is called as soon as it's loaded
509+
await lazyActionDeferred.resolve(action);
490510
await actionDeferred.resolve("LAZY ACTION");
511+
expect(action).toHaveBeenCalledTimes(1);
512+
expect(loader).toHaveBeenCalledTimes(0);
491513
expect(t.router.state.location.pathname).toBe("/");
492-
expect(t.router.state.navigation.state).toBe("loading");
493-
expect(t.router.state.actionData).toEqual({
494-
lazy: "LAZY ACTION",
495-
});
514+
expect(t.router.state.navigation.state).toBe("submitting");
515+
expect(t.router.state.actionData).toEqual(null);
496516
expect(t.router.state.loaderData).toEqual({});
497517

518+
// Finish loading all lazy properties
519+
await lazyLoaderDeferred.resolve(loader);
520+
expect(loader).toHaveBeenCalledTimes(1);
521+
expect(action).toHaveBeenCalledTimes(1);
522+
expect(t.router.state.location.pathname).toBe("/");
523+
expect(t.router.state.navigation.state).toBe("loading");
498524
await loaderDeferred.resolve("LAZY LOADER");
499525
expect(t.router.state.location.pathname).toBe("/lazy");
500526
expect(t.router.state.navigation.state).toBe("idle");
@@ -2028,7 +2054,7 @@ describe("lazily loaded route modules", () => {
20282054
expect(lazyStub).toHaveBeenCalledTimes(1);
20292055
});
20302056

2031-
it("handles errors when failing to resolve lazy route property on loading navigation", async () => {
2057+
it("handles errors when failing to resolve lazy route loader property on loading navigation", async () => {
20322058
let { lazyStub, lazyDeferred } = createLazyStub();
20332059
let routes = createBasicLazyRoutes({
20342060
loader: lazyStub,
@@ -2052,6 +2078,43 @@ describe("lazily loaded route modules", () => {
20522078
expect(lazyStub).toHaveBeenCalledTimes(1);
20532079
});
20542080

2081+
it("handles errors when failing to resolve other lazy route properties on loading navigation", async () => {
2082+
let { lazyStub: lazyLoader, lazyDeferred: lazyLoaderDeferred } =
2083+
createLazyStub();
2084+
let { lazyStub: lazyAction, lazyDeferred: lazyActionDeferred } =
2085+
createLazyStub();
2086+
let routes = createBasicLazyRoutes({
2087+
loader: lazyLoader,
2088+
action: lazyAction,
2089+
});
2090+
let t = setup({ routes });
2091+
expect(lazyLoader).not.toHaveBeenCalled();
2092+
2093+
await t.navigate("/lazy");
2094+
expect(t.router.state.location.pathname).toBe("/");
2095+
expect(t.router.state.navigation.state).toBe("loading");
2096+
expect(lazyLoader).toHaveBeenCalledTimes(1);
2097+
2098+
// Ensure loader is called as soon as it's loaded
2099+
let loader = jest.fn(() => null);
2100+
await lazyLoaderDeferred.resolve(loader);
2101+
expect(t.router.state.location.pathname).toBe("/");
2102+
expect(t.router.state.navigation.state).toBe("loading");
2103+
expect(loader).toHaveBeenCalledTimes(1);
2104+
2105+
// Reject remaining lazy properties
2106+
await lazyActionDeferred.reject(new Error("LAZY PROPERTY ERROR"));
2107+
expect(t.router.state.location.pathname).toBe("/lazy");
2108+
expect(t.router.state.navigation.state).toBe("idle");
2109+
2110+
expect(t.router.state.loaderData).toEqual({});
2111+
expect(t.router.state.errors).toEqual({
2112+
root: new Error("LAZY PROPERTY ERROR"),
2113+
});
2114+
expect(lazyLoader).toHaveBeenCalledTimes(1);
2115+
expect(lazyAction).toHaveBeenCalledTimes(1);
2116+
});
2117+
20552118
it("handles loader errors from lazy route functions when the route has an error boundary", async () => {
20562119
let { routes, lazyStub, lazyDeferred } = createBasicLazyFunctionRoutes();
20572120
let t = setup({ routes });
@@ -2305,7 +2368,7 @@ describe("lazily loaded route modules", () => {
23052368
it("handles errors when failing to resolve lazy route properties on submission navigation", async () => {
23062369
let { lazyStub, lazyDeferred } = createLazyStub();
23072370
let routes = createBasicLazyRoutes({
2308-
loader: lazyStub,
2371+
action: lazyStub,
23092372
});
23102373
let t = setup({ routes });
23112374
expect(lazyStub).not.toHaveBeenCalled();
@@ -2627,7 +2690,7 @@ describe("lazily loaded route modules", () => {
26272690
it("handles errors when failing to load lazy route properties on fetcher.submit", async () => {
26282691
let { lazyStub, lazyDeferred } = createLazyStub();
26292692
let routes = createBasicLazyRoutes({
2630-
loader: lazyStub,
2693+
action: lazyStub,
26312694
});
26322695
let t = setup({ routes });
26332696
expect(lazyStub).not.toHaveBeenCalled();

0 commit comments

Comments
 (0)