diff --git a/src/component/component_node.ts b/src/component/component_node.ts index ac673047..6c3ba1ac 100644 --- a/src/component/component_node.ts +++ b/src/component/component_node.ts @@ -21,9 +21,13 @@ export function component( let node: any = ctx.children[key]; let isDynamic = typeof name !== "string"; - if (node && node.status < STATUS.MOUNTED) { - node.destroy(); - node = undefined; + if (node) { + if (node.status < STATUS.MOUNTED) { + node.destroy(); + node = undefined; + } else if (node.status === STATUS.DESTROYED) { + node = undefined; + } } if (isDynamic && node && node.component.constructor !== name) { node = undefined; diff --git a/tests/components/__snapshots__/lifecycle.test.ts.snap b/tests/components/__snapshots__/lifecycle.test.ts.snap index 603e9c53..642ae2b0 100644 --- a/tests/components/__snapshots__/lifecycle.test.ts.snap +++ b/tests/components/__snapshots__/lifecycle.test.ts.snap @@ -425,6 +425,36 @@ exports[`lifecycle hooks mounted hook is called if mounted in DOM 1`] = ` }" `; +exports[`lifecycle hooks mounted hook is called on every mount, not just the first one 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers; + + let block1 = createBlock(\`
child
\`); + + return function template(ctx, node, key = \\"\\") { + return block1(); + } +}" +`; + +exports[`lifecycle hooks mounted hook is called on every mount, not just the first one 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers; + + return function template(ctx, node, key = \\"\\") { + let b2; + if (ctx['state'].hasChild) { + b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx); + } + return multi([b2]); + } +}" +`; + exports[`lifecycle hooks mounted hook is called on subcomponents, in proper order 1`] = ` "function anonymous(bdom, helpers ) { diff --git a/tests/components/lifecycle.test.ts b/tests/components/lifecycle.test.ts index f570caf8..1be37813 100644 --- a/tests/components/lifecycle.test.ts +++ b/tests/components/lifecycle.test.ts @@ -1064,4 +1064,54 @@ describe("lifecycle hooks", () => { "C:patched", ]); }); + + test("mounted hook is called on every mount, not just the first one", async () => { + const steps: string[] = []; + class Child extends Component { + static template = xml`
child
`; + setup() { + useLogLifecycle(steps) + } + } + + class Parent extends Component { + static template = xml``; + static components = { Child }; + state = useState({ hasChild: true }); + setup() { + useLogLifecycle(steps); + } + } + + const parent = await mount(Parent, fixture); + + parent.state.hasChild = false; + await nextTick(); + + parent.state.hasChild = true; + await nextTick(); + expect(steps).toEqual([ + "Parent:setup", + "Parent:willStart", + "Parent:render", + "Child:setup", + "Child:willStart", + "Child:render", + "Child:mounted", + "Parent:mounted", + "Parent:render", + "Parent:willPatch", + "Child:willUnmount", + "Child:destroyed", + "Parent:patched", + "Parent:render", + "Child:setup", + "Child:willStart", + "Child:render", + "Parent:willPatch", + "Child:mounted", + "Parent:patched" + ]); + Object.freeze(steps); + }); });