Skip to content

Commit ef312a3

Browse files
authored
fix: dynamically update deep scopedSlot refs (#899)
1 parent 4a4ed5d commit ef312a3

File tree

3 files changed

+48
-7
lines changed

3 files changed

+48
-7
lines changed

src/mixin.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
import { ref } from './apis'
1515
import vmStateManager from './utils/vmStateManager'
1616
import {
17-
updateTemplateRef,
17+
afterRender,
1818
activateCurrentInstance,
1919
resolveScopedSlots,
2020
asVmProperty,
@@ -31,16 +31,13 @@ export function mixin(Vue: VueConstructor) {
3131
Vue.mixin({
3232
beforeCreate: functionApiInit,
3333
mounted(this: ComponentInstance) {
34-
updateTemplateRef(this)
34+
afterRender(this)
3535
},
3636
beforeUpdate() {
3737
updateVmAttrs(this as ComponentInstance)
3838
},
3939
updated(this: ComponentInstance) {
40-
updateTemplateRef(this)
41-
if (this.$vnode?.context) {
42-
updateTemplateRef(this.$vnode.context)
43-
}
40+
afterRender(this)
4441
},
4542
})
4643

src/utils/instance.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { VNode } from 'vue'
12
import { ComponentInstance } from '../component'
23
import vmStateManager from './vmStateManager'
34
import {
@@ -76,7 +77,7 @@ export function asVmProperty(
7677
}
7778
}
7879

79-
export function updateTemplateRef(vm: ComponentInstance) {
80+
function updateTemplateRef(vm: ComponentInstance) {
8081
const rawBindings = vmStateManager.get(vm, 'rawBindings') || {}
8182
if (!rawBindings || !Object.keys(rawBindings).length) return
8283

@@ -103,6 +104,19 @@ export function updateTemplateRef(vm: ComponentInstance) {
103104
vmStateManager.set(vm, 'refs', validNewKeys)
104105
}
105106

107+
export function afterRender(vm: ComponentInstance) {
108+
const stack = [(vm as any)._vnode as VNode]
109+
while (stack.length) {
110+
const vnode = stack.pop()!
111+
if (vnode.context) updateTemplateRef(vnode.context)
112+
if (vnode.children) {
113+
for (let i = 0; i < vnode.children.length; ++i) {
114+
stack.push(vnode.children[i])
115+
}
116+
}
117+
}
118+
}
119+
106120
export function updateVmAttrs(vm: ComponentInstance, ctx?: SetupContext) {
107121
if (!vm) {
108122
return

test/templateRefs.spec.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,36 @@ describe('ref', () => {
113113
//@ts-ignore
114114
expect(vm.$refs.barRef).toBe(vm.barRef)
115115
})
116+
117+
it('should update deeply nested component refs using scoped slots', async () => {
118+
const vm = new Vue({
119+
setup() {
120+
const divRef = ref(null)
121+
const showDiv = ref(false)
122+
return {
123+
divRef,
124+
showDiv,
125+
}
126+
},
127+
template: `<div><foo #default>Slot: <div ref="divRef" v-if="showDiv" /></foo></div>`,
128+
components: {
129+
foo: {
130+
components: {
131+
bar: {
132+
template: `<div><slot /></div>`,
133+
},
134+
},
135+
template: '<div><bar #default><slot /></bar></div>',
136+
},
137+
},
138+
}).$mount()
139+
await nextTick()
140+
//@ts-ignore
141+
vm.showDiv = true
142+
await nextTick()
143+
//@ts-ignore
144+
expect(vm.$refs.divRef).toBe(vm.divRef)
145+
})
116146
// TODO: how ?
117147
// it('work with createElement', () => {
118148
// let root;

0 commit comments

Comments
 (0)