Skip to content

Commit 39524e0

Browse files
committed
Handle case if user forgets to await the handler
1 parent 66357d4 commit 39524e0

File tree

2 files changed

+44
-5
lines changed

2 files changed

+44
-5
lines changed

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,42 @@ describe("instrumentation", () => {
13841384
errorSpy.mockRestore();
13851385
});
13861386

1387+
it("waits for handler to finish if you forget to await the handler", async () => {
1388+
let t = setup({
1389+
routes: [
1390+
{
1391+
index: true,
1392+
},
1393+
{
1394+
id: "page",
1395+
path: "/page",
1396+
loader: true,
1397+
},
1398+
],
1399+
unstable_instrumentations: [
1400+
{
1401+
route(route) {
1402+
route.instrument({
1403+
async loader(loader) {
1404+
loader();
1405+
},
1406+
});
1407+
},
1408+
},
1409+
],
1410+
});
1411+
1412+
let A = await t.navigate("/page");
1413+
await A.loaders.page.resolve("PAGE");
1414+
expect(t.router.state).toMatchObject({
1415+
navigation: { state: "idle" },
1416+
location: { pathname: "/page" },
1417+
loaderData: { page: "PAGE" },
1418+
errors: null,
1419+
});
1420+
expect(A.loaders.page.stub).toHaveBeenCalledTimes(1);
1421+
});
1422+
13871423
it("does not let you call handlers more than once", async () => {
13881424
let errorSpy = jest.spyOn(console, "error").mockImplementation(() => {});
13891425
let t = setup({

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -401,14 +401,14 @@ async function recurseRight<T extends InstrumentationInfo>(
401401
} else {
402402
// If they forget to call the handler, or if they throw before calling the
403403
// handler, we need to ensure the handlers still gets called
404-
let handlerCalled = false;
404+
let handlerPromise: ReturnType<typeof recurseRight> | undefined = undefined;
405405
let callHandler = async (): Promise<InstrumentResult> => {
406-
if (handlerCalled) {
406+
if (handlerPromise) {
407407
console.error("You cannot call instrumented handlers more than once");
408408
} else {
409-
handlerCalled = true;
410-
result = await recurseRight(impls, info, handler, index - 1);
409+
handlerPromise = recurseRight(impls, info, handler, index - 1);
411410
}
411+
result = await handlerPromise;
412412
invariant(result, "Expected a result");
413413
if (result.type === "error" && result.value instanceof Error) {
414414
return { status: "error", error: result.value };
@@ -422,9 +422,12 @@ async function recurseRight<T extends InstrumentationInfo>(
422422
console.error("An instrumentation function threw an error:", e);
423423
}
424424

425-
if (!handlerCalled) {
425+
if (!handlerPromise) {
426426
await callHandler();
427427
}
428+
429+
// If the user forgot to await the handler, we can wait for it to resolve here
430+
await handlerPromise;
428431
}
429432

430433
if (result) {

0 commit comments

Comments
 (0)