[FIX] component: protect against errors in onWillDestroy

This commit is contained in:
Géry Debongnie
2022-03-31 10:35:09 +02:00
committed by Sam Degueldre
parent decf42c742
commit 7fb166bd50
6 changed files with 204 additions and 18 deletions
+11 -7
View File
@@ -80,10 +80,7 @@ export function component<P extends object>(
let isDynamic = typeof name !== "string";
if (node) {
if (node.status < STATUS.MOUNTED) {
node.destroy();
node = undefined;
} else if (node.status === STATUS.DESTROYED) {
if (node.status === STATUS.DESTROYED) {
node = undefined;
}
}
@@ -194,7 +191,7 @@ export class ComponentNode<P extends object = any, E = any> implements VNode<Com
async render(deep: boolean = false) {
let current = this.fiber;
if (current && current.root!.locked) {
if (current && (current.root!.locked || (current as any).bdom === true)) {
await Promise.resolve();
// situation may have changed after the microtask tick
current = this.fiber;
@@ -216,6 +213,7 @@ export class ComponentNode<P extends object = any, E = any> implements VNode<Com
const fiber = makeRootFiber(this);
fiber.deep = deep;
this.fiber = fiber;
this.app.scheduler.addFiber(fiber);
await Promise.resolve();
if (this.status === STATUS.DESTROYED) {
@@ -255,8 +253,14 @@ export class ComponentNode<P extends object = any, E = any> implements VNode<Com
for (let child of Object.values(this.children)) {
child._destroy();
}
for (let cb of this.willDestroy) {
cb.call(component);
if (this.willDestroy.length) {
try {
for (let cb of this.willDestroy) {
cb.call(component);
}
} catch (e) {
handleError({ error: e, node: this });
}
}
this.status = STATUS.DESTROYED;
}
+5 -9
View File
@@ -5,7 +5,7 @@ import type { Fiber } from "./fibers";
export const fibersInError: WeakMap<Fiber, any> = new WeakMap();
export const nodeErrorHandlers: WeakMap<ComponentNode, ((error: any) => void)[]> = new WeakMap();
function _handleError(node: ComponentNode | null, error: any, isFirstRound = false): boolean {
function _handleError(node: ComponentNode | null, error: any): boolean {
if (!node) {
return false;
}
@@ -16,23 +16,19 @@ function _handleError(node: ComponentNode | null, error: any, isFirstRound = fal
const errorHandlers = nodeErrorHandlers.get(node);
if (errorHandlers) {
let stopped = false;
let handled = false;
// execute in the opposite order
for (let i = errorHandlers.length - 1; i >= 0; i--) {
try {
errorHandlers[i](error);
stopped = true;
handled = true;
break;
} catch (e) {
error = e;
}
}
if (stopped) {
if (isFirstRound && fiber && fiber.node.fiber) {
const root = fiber.root!;
root.setCounter(root.counter - 1);
}
if (handled) {
return true;
}
}
@@ -55,7 +51,7 @@ export function handleError(params: ErrorParams) {
fibersInError.set(fiber.root!, error);
const handled = _handleError(node, error, true);
const handled = _handleError(node, error);
if (!handled) {
console.warn(`[Owl] Unhandled error. Destroying the root component`);
try {
+2 -1
View File
@@ -124,11 +124,12 @@ export class Fiber {
const root = this.root;
if (root) {
try {
(this.bdom as any) = true;
this.bdom = node.renderFn();
root.setCounter(root.counter - 1);
} catch (e) {
handleError({ node, error: e });
}
root.setCounter(root.counter - 1);
}
}
}