mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[FIX] components: prevent rendering destroyed children in some cases
This commit is contained in:
committed by
Sam Degueldre
parent
3e4ebb6378
commit
24b1ea7604
@@ -1569,6 +1569,70 @@ exports[`rendering parent twice, with different props on child and stuff 2`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`renderings, destruction, patch, stuff, ... yet another variation 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const b2 = text(\`A\`);
|
||||
const b3 = component(\`B\`, {value: ctx['state'].value}, key + \`__1\`, node, ctx);
|
||||
const b4 = component(\`D\`, {}, key + \`__2\`, node, ctx);
|
||||
return multi([b2, b3, b4]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`renderings, destruction, patch, stuff, ... yet another variation 2`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3;
|
||||
b2 = text(\`B\`);
|
||||
if (ctx['props'].value===33) {
|
||||
b3 = component(\`C\`, {}, key + \`__1\`, node, ctx);
|
||||
}
|
||||
return multi([b2, b3]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`renderings, destruction, patch, stuff, ... yet another variation 3`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||
|
||||
let block3 = createBlock(\`<p block-handler-0=\\"click\\"><block-text-1/></p>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const b2 = text(\`D\`);
|
||||
let hdlr1 = [ctx['increment'], ctx];
|
||||
let txt1 = ctx['state'].val;
|
||||
const b3 = block3([hdlr1, txt1]);
|
||||
return multi([b2, b3]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`renderings, destruction, patch, stuff, ... yet another variation 4`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||
|
||||
let block3 = createBlock(\`<span block-handler-0=\\"click\\"><block-text-1/></span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const b2 = text(\`C\`);
|
||||
let hdlr1 = [ctx['increment'], ctx];
|
||||
let txt1 = ctx['state'].val;
|
||||
const b3 = block3([hdlr1, txt1]);
|
||||
return multi([b2, b3]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-foreach with dynamic async component 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
|
||||
@@ -3883,6 +3883,107 @@ test("delayed rendering, destruction, stuff happens", async () => {
|
||||
expect(fixture.innerHTML).toBe("AB");
|
||||
});
|
||||
|
||||
test("renderings, destruction, patch, stuff, ... yet another variation", async () => {
|
||||
const promB = makeDeferred();
|
||||
|
||||
class D extends Component {
|
||||
static template = xml`D<p t-on-click="increment"><t t-esc="state.val"/></p>`;
|
||||
state = useState({ val: 1 });
|
||||
setup() {
|
||||
useLogLifecycle();
|
||||
}
|
||||
increment() {
|
||||
this.state.val++;
|
||||
}
|
||||
}
|
||||
|
||||
// almost the same as D
|
||||
class C extends Component {
|
||||
static template = xml`C<span t-on-click="increment"><t t-esc="state.val"/></span>`;
|
||||
state = useState({ val: 1 });
|
||||
setup() {
|
||||
useLogLifecycle();
|
||||
}
|
||||
increment() {
|
||||
this.state.val++;
|
||||
}
|
||||
}
|
||||
|
||||
class B extends Component {
|
||||
static template = xml`B<t t-if="props.value === 33"><C/></t>`;
|
||||
static components = { C };
|
||||
setup() {
|
||||
useLogLifecycle();
|
||||
onWillUpdateProps(() => promB);
|
||||
}
|
||||
}
|
||||
|
||||
class A extends Component {
|
||||
static template = xml`A<B value="state.value"/><D/>`;
|
||||
static components = { B, D };
|
||||
state = useState({ value: 33 });
|
||||
setup() {
|
||||
useLogLifecycle();
|
||||
}
|
||||
}
|
||||
|
||||
const parent = await mount(A, fixture);
|
||||
expect(fixture.innerHTML).toBe("ABC<span>1</span>D<p>1</p>");
|
||||
expect([
|
||||
"A:setup",
|
||||
"A:willStart",
|
||||
"A:willRender",
|
||||
"B:setup",
|
||||
"B:willStart",
|
||||
"D:setup",
|
||||
"D:willStart",
|
||||
"A:rendered",
|
||||
"B:willRender",
|
||||
"C:setup",
|
||||
"C:willStart",
|
||||
"B:rendered",
|
||||
"D:willRender",
|
||||
"D:rendered",
|
||||
"C:willRender",
|
||||
"C:rendered",
|
||||
"C:mounted",
|
||||
"D:mounted",
|
||||
"B:mounted",
|
||||
"A:mounted",
|
||||
]).toBeLogged();
|
||||
|
||||
// render in A, it updates B, will remove C, stopped in B
|
||||
parent.state.value = 50;
|
||||
await nextTick();
|
||||
expect(["A:willRender", "B:willUpdateProps", "A:rendered"]).toBeLogged();
|
||||
|
||||
// update C => render should be delayed, because AB is currently rendering
|
||||
fixture.querySelector("span")!.click();
|
||||
await nextTick();
|
||||
expect([]).toBeLogged();
|
||||
|
||||
// resolve prom B => render is done, component C is destroyed
|
||||
promB.resolve();
|
||||
await nextTick();
|
||||
expect([
|
||||
"B:willRender",
|
||||
"B:rendered",
|
||||
"A:willPatch",
|
||||
"B:willPatch",
|
||||
"C:willUnmount",
|
||||
"C:willDestroy",
|
||||
"B:patched",
|
||||
"A:patched",
|
||||
]).toBeLogged();
|
||||
expect(fixture.innerHTML).toBe("ABD<p>1</p>");
|
||||
|
||||
// update D => should just render completely independently
|
||||
fixture.querySelector("p")!.click();
|
||||
await nextTick();
|
||||
expect(["D:willRender", "D:rendered", "D:willPatch", "D:patched"]).toBeLogged();
|
||||
expect(fixture.innerHTML).toBe("ABD<p>2</p>");
|
||||
});
|
||||
|
||||
// test.skip("components with shouldUpdate=false", async () => {
|
||||
// const state = { p: 1, cc: 10 };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user