Skip to content

Commit 05588d2

Browse files
authored
Don't run loaders below the boundary during partial hydration (#11324)
1 parent 98e7f7b commit 05588d2

File tree

3 files changed

+78
-8
lines changed

3 files changed

+78
-8
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@remix-run/router": patch
3+
---
4+
5+
Fix a `future.v7_partialHydration` bug that would re-run loaders below the boundary on hydration if SSR loader errors bubbled to a parent boundary

packages/router/__tests__/route-fallback-test.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ describe("future.v7_partialHydration", () => {
401401
});
402402
});
403403

404-
it("does not kick off initial data load if errors exist", async () => {
404+
it("does not kick off initial data load if errors exist (parent error)", async () => {
405405
let consoleWarnSpy = jest
406406
.spyOn(console, "warn")
407407
.mockImplementation(() => {});
@@ -457,5 +457,62 @@ describe("future.v7_partialHydration", () => {
457457
router.dispose();
458458
consoleWarnSpy.mockReset();
459459
});
460+
461+
it("does not kick off initial data load if errors exist (bubbled child error)", async () => {
462+
let consoleWarnSpy = jest
463+
.spyOn(console, "warn")
464+
.mockImplementation(() => {});
465+
let parentDfd = createDeferred();
466+
let parentSpy = jest.fn(() => parentDfd.promise);
467+
let childDfd = createDeferred();
468+
let childSpy = jest.fn(() => childDfd.promise);
469+
let router = createRouter({
470+
history: createMemoryHistory({ initialEntries: ["/child"] }),
471+
routes: [
472+
{
473+
path: "/",
474+
loader: parentSpy,
475+
children: [
476+
{
477+
path: "child",
478+
loader: childSpy,
479+
},
480+
],
481+
},
482+
],
483+
future: {
484+
v7_partialHydration: true,
485+
},
486+
hydrationData: {
487+
errors: {
488+
"0": "CHILD ERROR",
489+
},
490+
loaderData: {
491+
"0": "PARENT DATA",
492+
},
493+
},
494+
});
495+
router.initialize();
496+
497+
expect(consoleWarnSpy).not.toHaveBeenCalled();
498+
expect(parentSpy).not.toHaveBeenCalled();
499+
expect(childSpy).not.toHaveBeenCalled();
500+
expect(router.state).toMatchObject({
501+
historyAction: "POP",
502+
location: expect.objectContaining({ pathname: "/child" }),
503+
matches: [{ route: { path: "/" } }, { route: { path: "child" } }],
504+
initialized: true,
505+
navigation: IDLE_NAVIGATION,
506+
errors: {
507+
"0": "CHILD ERROR",
508+
},
509+
loaderData: {
510+
"0": "PARENT DATA",
511+
},
512+
});
513+
514+
router.dispose();
515+
consoleWarnSpy.mockReset();
516+
});
460517
});
461518
});

packages/router/router.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -833,13 +833,21 @@ export function createRouter(init: RouterInit): Router {
833833
// were marked for explicit hydration
834834
let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
835835
let errors = init.hydrationData ? init.hydrationData.errors : null;
836-
initialized = initialMatches.every(
837-
(m) =>
838-
m.route.loader &&
839-
m.route.loader.hydrate !== true &&
840-
((loaderData && loaderData[m.route.id] !== undefined) ||
841-
(errors && errors[m.route.id] !== undefined))
842-
);
836+
let isRouteInitialized = (m: AgnosticDataRouteMatch) =>
837+
m.route.loader &&
838+
m.route.loader.hydrate !== true &&
839+
((loaderData && loaderData[m.route.id] !== undefined) ||
840+
(errors && errors[m.route.id] !== undefined));
841+
842+
// If errors exist, don't consider routes below the boundary
843+
if (errors) {
844+
let idx = initialMatches.findIndex(
845+
(m) => errors![m.route.id] !== undefined
846+
);
847+
initialized = initialMatches.slice(0, idx + 1).every(isRouteInitialized);
848+
} else {
849+
initialized = initialMatches.every(isRouteInitialized);
850+
}
843851
} else {
844852
// Without partial hydration - we're initialized if we were provided any
845853
// hydrationData - which is expected to be complete

0 commit comments

Comments
 (0)