Skip to content

Commit c194f4a

Browse files
authored
Fix error boundary tracking for multiple errors bubbling to the same boundary (#9702)
* Fix error boundary tracking for multiple errors bubbling to the same boundary * Add changeset
1 parent 6c8a56f commit c194f4a

File tree

3 files changed

+68
-3
lines changed

3 files changed

+68
-3
lines changed

.changeset/violet-rules-rest.md

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 error boundary tracking for multiple errors bubbling to the same boundary

packages/router/__tests__/router-test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10774,6 +10774,61 @@ describe("a router", () => {
1077410774
});
1077510775
});
1077610776

10777+
it("should handle multiple errors at separate boundaries", async () => {
10778+
let routes = [
10779+
{
10780+
id: "root",
10781+
path: "/",
10782+
loader: () => Promise.reject("ROOT"),
10783+
hasErrorBoundary: true,
10784+
children: [
10785+
{
10786+
id: "child",
10787+
path: "child",
10788+
loader: () => Promise.reject("CHILD"),
10789+
hasErrorBoundary: true,
10790+
},
10791+
],
10792+
},
10793+
];
10794+
10795+
let { query } = createStaticHandler(routes);
10796+
let context;
10797+
10798+
context = await query(createRequest("/child"));
10799+
expect(context.errors).toEqual({
10800+
root: "ROOT",
10801+
child: "CHILD",
10802+
});
10803+
});
10804+
10805+
it("should handle multiple errors at the same boundary", async () => {
10806+
let routes = [
10807+
{
10808+
id: "root",
10809+
path: "/",
10810+
loader: () => Promise.reject("ROOT"),
10811+
hasErrorBoundary: true,
10812+
children: [
10813+
{
10814+
id: "child",
10815+
path: "child",
10816+
loader: () => Promise.reject("CHILD"),
10817+
},
10818+
],
10819+
},
10820+
];
10821+
10822+
let { query } = createStaticHandler(routes);
10823+
let context;
10824+
10825+
context = await query(createRequest("/child"));
10826+
expect(context.errors).toEqual({
10827+
// higher error value wins
10828+
root: "ROOT",
10829+
});
10830+
});
10831+
1077710832
it("should handle aborted load requests", async () => {
1077810833
let dfd = createDeferred();
1077910834
let controller = new AbortController();

packages/router/router.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2848,9 +2848,14 @@ function processRouteLoaderData(
28482848
error = Object.values(pendingError)[0];
28492849
pendingError = undefined;
28502850
}
2851-
errors = Object.assign(errors || {}, {
2852-
[boundaryMatch.route.id]: error,
2853-
});
2851+
2852+
errors = errors || {};
2853+
2854+
// Prefer higher error values if lower errors bubble to the same boundary
2855+
if (errors[boundaryMatch.route.id] == null) {
2856+
errors[boundaryMatch.route.id] = error;
2857+
}
2858+
28542859
// Once we find our first (highest) error, we set the status code and
28552860
// prevent deeper status codes from overriding
28562861
if (!foundError) {

0 commit comments

Comments
 (0)