[FIX] component: correctly create a new node when previous is destroyed

Previously, when a component node had been created and destroyed, and
the corresponding component was then recreated, the destroyed node was
reused. This commit fixes that
This commit is contained in:
Samuel Degueldre
2021-10-22 09:53:36 +02:00
committed by Aaron Bohy
parent ced5d0f69f
commit 15e4c856da
3 changed files with 87 additions and 3 deletions
+7 -3
View File
@@ -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;
@@ -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(\`<div>child</div>\`);
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
) {
+50
View File
@@ -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`<div>child</div>`;
setup() {
useLogLifecycle(steps)
}
}
class Parent extends Component {
static template = xml`<Child t-if="state.hasChild"/>`;
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);
});
});