fix(runtime-core/template-ref): template ref used in the same template should trigger update

fix #1505
This commit is contained in:
Evan You 2020-07-06 16:40:00 -04:00
parent 64e2f46436
commit 36b6b4f022
3 changed files with 38 additions and 8 deletions

View File

@ -5,7 +5,8 @@ import {
render, render,
nextTick, nextTick,
defineComponent, defineComponent,
reactive reactive,
serializeInner
} from '@vue/runtime-test' } from '@vue/runtime-test'
// reference: https://vue-composition-api-rfc.netlify.com/api.html#template-refs // 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(refKey2.value).toBe(root.children[2])
expect(refKey3.value).toBe(root.children[3]) 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(`<div id="foo"></div>`)
await nextTick()
// ref should be updated
expect(serializeInner(root)).toBe(`<div id="foo">foo</div>`)
})
}) })

View File

@ -227,7 +227,7 @@ export function createHydrationFunctions(
} }
if (ref != null && parentComponent) { if (ref != null && parentComponent) {
setRef(ref, null, parentComponent, vnode) setRef(ref, null, parentComponent, parentSuspense, vnode)
} }
return nextNode return nextNode

View File

@ -275,7 +275,8 @@ export const queuePostRenderEffect = __FEATURE_SUSPENSE__
export const setRef = ( export const setRef = (
rawRef: VNodeNormalizedRef, rawRef: VNodeNormalizedRef,
oldRawRef: VNodeNormalizedRef | null, oldRawRef: VNodeNormalizedRef | null,
parent: ComponentInternalInstance, parentComponent: ComponentInternalInstance,
parentSuspense: SuspenseBoundary | null,
vnode: VNode | null vnode: VNode | null
) => { ) => {
let value: ComponentPublicInstance | RendererNode | null let value: ComponentPublicInstance | RendererNode | null
@ -306,7 +307,9 @@ export const setRef = (
if (isString(oldRef)) { if (isString(oldRef)) {
refs[oldRef] = null refs[oldRef] = null
if (hasOwn(setupState, oldRef)) { if (hasOwn(setupState, oldRef)) {
setupState[oldRef] = null queuePostRenderEffect(() => {
setupState[oldRef] = null
}, parentSuspense)
} }
} else if (isRef(oldRef)) { } else if (isRef(oldRef)) {
oldRef.value = null oldRef.value = null
@ -316,12 +319,17 @@ export const setRef = (
if (isString(ref)) { if (isString(ref)) {
refs[ref] = value refs[ref] = value
if (hasOwn(setupState, ref)) { if (hasOwn(setupState, ref)) {
setupState[ref] = value queuePostRenderEffect(() => {
setupState[ref] = value
}, parentSuspense)
} }
} else if (isRef(ref)) { } else if (isRef(ref)) {
ref.value = value ref.value = value
} else if (isFunction(ref)) { } else if (isFunction(ref)) {
callWithErrorHandling(ref, parent, ErrorCodes.FUNCTION_REF, [value, refs]) callWithErrorHandling(ref, parentComponent, ErrorCodes.FUNCTION_REF, [
value,
refs
])
} else if (__DEV__) { } else if (__DEV__) {
warn('Invalid template ref type:', value, `(${typeof value})`) warn('Invalid template ref type:', value, `(${typeof value})`)
} }
@ -497,7 +505,7 @@ function baseCreateRenderer(
// set ref // set ref
if (ref != null && parentComponent) { 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 } = vnode
// unset ref // unset ref
if (ref != null && parentComponent) { if (ref != null && parentComponent) {
setRef(ref, null, parentComponent, null) setRef(ref, null, parentComponent, parentSuspense, null)
} }
if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) { if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {