diff --git a/doc/component.md b/doc/component.md index ea7328b4..797ad3d0 100644 --- a/doc/component.md +++ b/doc/component.md @@ -200,7 +200,7 @@ a owl component: | ------------------------------------------------ | --------------------------------------- | | **[constructor](#constructor)** | constructor | | **[willStart](#willStart)** | async, before first rendering | -| **[mounted](#mounted)** | when component is render and in DOM | +| **[mounted](#mounted)** | just after component is rendered and added to the DOM | | **[willUpdateProps](#willupdatepropsnextprops)** | async, before props update | | **[willPatch](#willpatch)** | just before the DOM is patched | | **[patched](#patchedsnapshot)** | just after the DOM is patched | diff --git a/src/component.ts b/src/component.ts index c7cae77c..b76a41cb 100644 --- a/src/component.ts +++ b/src/component.ts @@ -248,7 +248,8 @@ export class Component< const renderVDom = this._render(force, patchQueue); const renderId = this.__owl__.renderId; const vnode = await renderVDom; - if (renderId === this.__owl__.renderId) { + + if (this.__owl__.isMounted && renderId === this.__owl__.renderId) { // we only update the vnode and the actual DOM if no other rendering // occurred between now and when the render method was initially called. if (shouldCallPatchHooks) { diff --git a/tests/component.test.ts b/tests/component.test.ts index 8204358f..0a2185b4 100644 --- a/tests/component.test.ts +++ b/tests/component.test.ts @@ -1645,6 +1645,59 @@ describe("async rendering", () => { "
a2b2
" ); }); + + test.only("properly behave when destroyed/unmounted while rendering ", async () => { + let def = Promise.resolve(); + + class Child extends Widget { + inlineTemplate = `
`; + widgets = { SubChild }; + mounted() { + // from now on, each rendering in child widget will be delayed (see + // _render) + def = makeDeferred(); + } + async _render(f, p) { + const result = await super._render(f, p); + await def; + return result; + } + } + + class SubChild extends Widget { + willPatch() { + throw new Error("Should not happen!"); + } + patched() { + throw new Error("Should not happen!"); + } + } + + class Parent extends Widget { + inlineTemplate = ` +
`; + widgets = { Child }; + state = { flag: true, val: "Framboise Lindemans" }; + } + const parent = new Parent(env); + await parent.mount(fixture); + expect(fixture.innerHTML).toBe("
"); + + // this change triggers a rendering of the parent. This rendering is delayed, + // because child is now waiting for def to be resolved + parent.state.val = "Framboise Girardin"; + await nextTick(); + + // with this, we remove child, and childchild, even though it is not finished + // rendering from previous changes + parent.state.flag = false; + await nextTick(); + + // we now resolve def, so the child rendering is now complete. + (def).resolve(); + await nextTick(); + expect(fixture.innerHTML).toBe("
"); + }); }); describe("updating environment", () => {