diff --git a/src/runtime/scheduler.ts b/src/runtime/scheduler.ts
index 72079dfed..ed1d14c00 100644
--- a/src/runtime/scheduler.ts
+++ b/src/runtime/scheduler.ts
@@ -93,7 +93,12 @@ export class Scheduler {
if (!hasError) {
fiber.complete();
}
- this.tasks.delete(fiber);
+ if (fiber.appliedToDom) {
+ // in this case, the fiber has been recycled by some rendering triggered
+ // in a error handler. so we actually want to keep the fiber around,
+ // otherwise the new render will just never be applied
+ this.tasks.delete(fiber);
+ }
}
}
}
diff --git a/tests/components/__snapshots__/error_handling.test.ts.snap b/tests/components/__snapshots__/error_handling.test.ts.snap
index d674b9750..45f1bf43d 100644
--- a/tests/components/__snapshots__/error_handling.test.ts.snap
+++ b/tests/components/__snapshots__/error_handling.test.ts.snap
@@ -320,6 +320,50 @@ exports[`can catch errors can catch an error in a component render function 3`]
}"
`;
+exports[`can catch errors can catch an error in onmounted 1`] = `
+"function anonymous(app, bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, comment } = bdom;
+ const comp1 = app.createComponent(null, false, false, false, []);
+
+ return function template(ctx, node, key = \\"\\") {
+ let b2, b3;
+ b2 = text(\`Main\`);
+ if (ctx['state'].ok) {
+ const Comp1 = ctx['component'];
+ b3 = toggler(Comp1, comp1({}, (Comp1).name + key + \`__1\`, node, this, Comp1));
+ }
+ return multi([b2, b3]);
+ }
+}"
+`;
+
+exports[`can catch errors can catch an error in onmounted 3`] = `
+"function anonymous(app, bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, comment } = bdom;
+
+ let block1 = createBlock(\`
Error!!!
\`);
+
+ return function template(ctx, node, key = \\"\\") {
+ return block1();
+ }
+}"
+`;
+
+exports[`can catch errors can catch an error in onmounted 4`] = `
+"function anonymous(app, bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, comment } = bdom;
+
+ let block1 = createBlock(\`perfect
\`);
+
+ return function template(ctx, node, key = \\"\\") {
+ return block1();
+ }
+}"
+`;
+
exports[`can catch errors can catch an error in the constructor call of a component render function 1`] = `
"function anonymous(app, bdom, helpers
) {
diff --git a/tests/components/error_handling.test.ts b/tests/components/error_handling.test.ts
index 9b45ea78c..703b29183 100644
--- a/tests/components/error_handling.test.ts
+++ b/tests/components/error_handling.test.ts
@@ -564,6 +564,82 @@ describe("can catch errors", () => {
expect(mockConsoleWarn).toBeCalledTimes(0);
});
+ test("can catch an error in onmounted", async () => {
+ class ErrorComponent extends Component {
+ static template = xml`Error!!!
`;
+ setup() {
+ useLogLifecycle();
+ onMounted(() => {
+ throw new Error("error");
+ });
+ }
+ }
+ class PerfectComponent extends Component {
+ static template = xml`perfect
`;
+ setup() {
+ useLogLifecycle();
+ }
+ }
+ class Main extends Component {
+ static template = xml`Main`;
+ component: any;
+ state: any;
+ setup() {
+ this.state = useState({ ok: false });
+ useLogLifecycle();
+ this.component = ErrorComponent;
+ onError(() => {
+ this.component = PerfectComponent;
+ this.render();
+ });
+ }
+ }
+
+ const app = await mount(Main, fixture);
+ expect(steps.splice(0)).toMatchInlineSnapshot(`
+ Array [
+ "Main:setup",
+ "Main:willStart",
+ "Main:willRender",
+ "Main:rendered",
+ "Main:mounted",
+ ]
+ `);
+ expect(fixture.innerHTML).toBe("Main");
+ (app as any).state.ok = true;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("MainError!!!
");
+ expect(steps.splice(0)).toMatchInlineSnapshot(`
+ Array [
+ "Main:willRender",
+ "ErrorComponent:setup",
+ "ErrorComponent:willStart",
+ "Main:rendered",
+ "ErrorComponent:willRender",
+ "ErrorComponent:rendered",
+ "Main:willPatch",
+ "ErrorComponent:mounted",
+ "Main:willRender",
+ "PerfectComponent:setup",
+ "PerfectComponent:willStart",
+ "Main:rendered",
+ ]
+ `);
+ await nextTick();
+ expect(steps.splice(0)).toMatchInlineSnapshot(`
+ Array [
+ "PerfectComponent:willRender",
+ "PerfectComponent:rendered",
+ "Main:willPatch",
+ "ErrorComponent:willUnmount",
+ "ErrorComponent:willDestroy",
+ "PerfectComponent:mounted",
+ "Main:patched",
+ ]
+ `);
+ expect(fixture.innerHTML).toBe("Mainperfect
");
+ });
+
test("calling a hook outside setup should crash", async () => {
class Root extends Component {
static template = xml``;