diff --git a/src/core/vdom/helpers.js b/src/core/vdom/helpers.js index ef6f4bae556..eae33825bc9 100644 --- a/src/core/vdom/helpers.js +++ b/src/core/vdom/helpers.js @@ -7,12 +7,16 @@ export function normalizeChildren ( children: any, ns: string | void ): Array | void { - // invoke children thunks. - // components always receive their children as thunks so that they - // can perform the actual render inside their own dependency collection cycle. - if (typeof children === 'function') { + // Invoke children thunks. Components always receive their children + // as thunks so that they can perform the actual render inside their + // own dependency collection cycle. Also, since JSX automatically + // wraps component children in a thunk, we handle nested thunks to + // prevent situations such as { children } + // from failing when it produces a double thunk. + while (typeof children === 'function') { children = children() } + if (isPrimitive(children)) { return [createTextVNode(children)] } diff --git a/test/unit/features/options/render.spec.js b/test/unit/features/options/render.spec.js index 424bb9aebd2..052917083b5 100644 --- a/test/unit/features/options/render.spec.js +++ b/test/unit/features/options/render.spec.js @@ -36,4 +36,16 @@ describe('Options render', () => { new Vue().$mount() expect('Failed to mount component: template or render function not defined.').toHaveBeenWarned() }) + + // Since JSX automatically thunkifies children, this will + // prevent { children } from + // failing when it produces a double thunk. + it('should support nested thunk children', () => { + const vm = new Vue({ + render: h => h('div', + () => () => () => ['hello ', h('strong', 'world')] + ) + }).$mount() + expect(vm.$el.innerHTML).toBe('hello world') + }) })