diff --git a/packages/runtime-core/__tests__/apiTemplateRef.spec.ts b/packages/runtime-core/__tests__/apiTemplateRef.spec.ts index 20d074fa1f9..cb5da3a9533 100644 --- a/packages/runtime-core/__tests__/apiTemplateRef.spec.ts +++ b/packages/runtime-core/__tests__/apiTemplateRef.spec.ts @@ -5,7 +5,8 @@ import { render, nextTick, defineComponent, - reactive + reactive, + serializeInner } from '@vue/runtime-test' // reference: https://vue-composition-api-rfc.netlify.com/api.html#template-refs @@ -246,4 +247,25 @@ describe('api: template refs', () => { expect(refKey2.value).toBe(root.children[2]) expect(refKey3.value).toBe(root.children[3]) }) + + // #1505 + test('reactive template ref in the same template', async () => { + const Comp = { + setup() { + const el = ref() + return { el } + }, + render(this: any) { + return h('div', { id: 'foo', ref: 'el' }, this.el && this.el.props.id) + } + } + + const root = nodeOps.createElement('div') + render(h(Comp), root) + // ref not ready on first render, but should queue an update immediately + expect(serializeInner(root)).toBe(`
`) + await nextTick() + // ref should be updated + expect(serializeInner(root)).toBe(`
foo
`) + }) }) diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index b0d455b0af4..62d9e6f0a09 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -227,7 +227,7 @@ export function createHydrationFunctions( } if (ref != null && parentComponent) { - setRef(ref, null, parentComponent, vnode) + setRef(ref, null, parentComponent, parentSuspense, vnode) } return nextNode diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index de69659b050..2c827136a9f 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -275,7 +275,8 @@ export const queuePostRenderEffect = __FEATURE_SUSPENSE__ export const setRef = ( rawRef: VNodeNormalizedRef, oldRawRef: VNodeNormalizedRef | null, - parent: ComponentInternalInstance, + parentComponent: ComponentInternalInstance, + parentSuspense: SuspenseBoundary | null, vnode: VNode | null ) => { let value: ComponentPublicInstance | RendererNode | null @@ -306,7 +307,9 @@ export const setRef = ( if (isString(oldRef)) { refs[oldRef] = null if (hasOwn(setupState, oldRef)) { - setupState[oldRef] = null + queuePostRenderEffect(() => { + setupState[oldRef] = null + }, parentSuspense) } } else if (isRef(oldRef)) { oldRef.value = null @@ -316,12 +319,17 @@ export const setRef = ( if (isString(ref)) { refs[ref] = value if (hasOwn(setupState, ref)) { - setupState[ref] = value + queuePostRenderEffect(() => { + setupState[ref] = value + }, parentSuspense) } } else if (isRef(ref)) { ref.value = value } else if (isFunction(ref)) { - callWithErrorHandling(ref, parent, ErrorCodes.FUNCTION_REF, [value, refs]) + callWithErrorHandling(ref, parentComponent, ErrorCodes.FUNCTION_REF, [ + value, + refs + ]) } else if (__DEV__) { warn('Invalid template ref type:', value, `(${typeof value})`) } @@ -497,7 +505,7 @@ function baseCreateRenderer( // set ref if (ref != null && parentComponent) { - setRef(ref, n1 && n1.ref, parentComponent, n2) + setRef(ref, n1 && n1.ref, parentComponent, parentSuspense, n2) } } @@ -1868,7 +1876,7 @@ function baseCreateRenderer( } = vnode // unset ref if (ref != null && parentComponent) { - setRef(ref, null, parentComponent, null) + setRef(ref, null, parentComponent, parentSuspense, null) } if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {