diff --git a/packages/runtime-core/__tests__/rendererComponent.spec.ts b/packages/runtime-core/__tests__/rendererComponent.spec.ts
index c389e1e8354..3fdc388823d 100644
--- a/packages/runtime-core/__tests__/rendererComponent.spec.ts
+++ b/packages/runtime-core/__tests__/rendererComponent.spec.ts
@@ -324,4 +324,34 @@ describe('renderer: component', () => {
expect(serializeInner(root)).toBe(``)
expect(ids).toEqual([ids[0], ids[0] + 1, ids[0] + 2])
})
+
+ test('child component props update should not lead to double update', async () => {
+ const text = ref(0)
+ const spy = jest.fn()
+
+ const App = {
+ render() {
+ return h(Comp, { text: text.value })
+ }
+ }
+
+ const Comp = {
+ props: ['text'],
+ render(this: any) {
+ spy()
+ return h('h1', this.text)
+ }
+ }
+
+ const root = nodeOps.createElement('div')
+ render(h(App), root)
+
+ expect(serializeInner(root)).toBe(`
0
`)
+ expect(spy).toHaveBeenCalledTimes(1)
+
+ text.value++
+ await nextTick()
+ expect(serializeInner(root)).toBe(`1
`)
+ expect(spy).toHaveBeenCalledTimes(2)
+ })
})
diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts
index 17eaaf44d5c..76848cb52f3 100644
--- a/packages/runtime-core/src/renderer.ts
+++ b/packages/runtime-core/src/renderer.ts
@@ -1458,6 +1458,9 @@ function baseCreateRenderer(
pushWarningContext(next || instance.vnode)
}
+ // Disallow component effect recursion during pre-lifecycle hooks.
+ effect.allowRecurse = false
+
if (next) {
next.el = vnode.el
updateComponentPreRender(instance, next, optimized)
@@ -1465,8 +1468,6 @@ function baseCreateRenderer(
next = vnode
}
- // Disallow component effect recursion during pre-lifecycle hooks.
- effect.allowRecurse = false
// beforeUpdate hook
if (bu) {
invokeArrayFns(bu)
@@ -1481,6 +1482,7 @@ function baseCreateRenderer(
) {
instance.emit('hook:beforeUpdate')
}
+
effect.allowRecurse = true
// render