diff --git a/src/component/component_node.ts b/src/component/component_node.ts index 8dffc436..fc563640 100644 --- a/src/component/component_node.ts +++ b/src/component/component_node.ts @@ -201,19 +201,8 @@ export class ComponentNode async updateAndRender(props: any, parentFiber: Fiber) { // update - const root = this.fiber && this.fiber.root; const fiber = makeChildFiber(this, parentFiber); - const parentRoot = parentFiber.root; - const rootChanged = root !== parentRoot; this.fiber = fiber; - if (rootChanged) { - if (this.willPatch.length) { - parentRoot.willPatch.push(fiber); - } - if (this.patched.length) { - parentRoot.patched.push(fiber); - } - } const component = this.component; applyDefaultProps(props, component.constructor as any); const prom = Promise.all(this.willUpdateProps.map((f) => f.call(component, props))); @@ -221,8 +210,15 @@ export class ComponentNode if (fiber !== this.fiber) { return; } - this.component.props = props; + component.props = props; this._render(fiber); + const parentRoot = parentFiber.root; + if (this.willPatch.length) { + parentRoot.willPatch.push(fiber); + } + if (this.patched.length) { + parentRoot.patched.push(fiber); + } } /** diff --git a/tests/components/__snapshots__/concurrency.test.ts.snap b/tests/components/__snapshots__/concurrency.test.ts.snap index 13e75694..83e0b110 100644 --- a/tests/components/__snapshots__/concurrency.test.ts.snap +++ b/tests/components/__snapshots__/concurrency.test.ts.snap @@ -1175,6 +1175,28 @@ exports[`two renderings initiated between willPatch and patched 2`] = ` }" `; +exports[`two sequential renderings before an animation frame 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(ctx['props'].value); + } +}" +`; + +exports[`two sequential renderings before an animation frame 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return component(\`Child\`, {value: ctx['state'].value}, key + \`__1\`, node, ctx); + } +}" +`; + exports[`update a sub-component twice in the same frame 1`] = ` "function anonymous(bdom, helpers ) { diff --git a/tests/components/concurrency.test.ts b/tests/components/concurrency.test.ts index a64832c4..a35f7272 100644 --- a/tests/components/concurrency.test.ts +++ b/tests/components/concurrency.test.ts @@ -2803,6 +2803,72 @@ test("delay willUpdateProps with rendering grandchild", async () => { ]).toBeLogged(); }); +test("two sequential renderings before an animation frame", async () => { + class Child extends Component { + static template = xml``; + setup() { + useLogLifecycle(); + } + } + + class Parent extends Component { + static template = xml``; + static components = { Child }; + state = useState({ value: 0 }); + setup() { + useLogLifecycle(); + } + } + const parent = await mount(Parent, fixture); + expect(fixture.innerHTML).toBe("0"); + expect([ + "Parent:setup", + "Parent:willStart", + "Parent:willRender", + "Child:setup", + "Child:willStart", + "Parent:rendered", + "Child:willRender", + "Child:rendered", + "Child:mounted", + "Parent:mounted", + ]).toBeLogged(); + + parent.state.value = 1; + await nextMicroTick(); + await nextMicroTick(); + await nextMicroTick(); + await nextMicroTick(); + await nextMicroTick(); + expect(fixture.innerHTML).toBe("0"); + expect([ + "Parent:willRender", + "Child:willUpdateProps", + "Parent:rendered", + "Child:willRender", + "Child:rendered", + ]).toBeLogged(); + + parent.state.value = 2; + // enough microticks to wait for render + willupdateprops + await nextMicroTick(); + await nextMicroTick(); + await nextMicroTick(); + await nextMicroTick(); + await nextMicroTick(); + expect(fixture.innerHTML).toBe("0"); + expect([ + "Parent:willRender", + "Child:willUpdateProps", + "Parent:rendered", + "Child:willRender", + "Child:rendered", + ]).toBeLogged(); + + await nextTick(); + // we check here that the willPatch and patched hooks are called only once + expect(["Parent:willPatch", "Child:willPatch", "Child:patched", "Parent:patched"]).toBeLogged(); +}); // test.skip("components with shouldUpdate=false", async () => { // const state = { p: 1, cc: 10 };