Skip to content

Commit

Permalink
fix: changing prop causes rerender to lose attributes #840 (#843)
Browse files Browse the repository at this point in the history
Co-authored-by: neiyichao03 <nieyichao03@kuaishou.com>
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
3 people authored Nov 3, 2021
1 parent dc74c4d commit a43090d
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/mixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function mixin(Vue: VueConstructor) {
updateTemplateRef(this)
},
beforeUpdate() {
updateVmAttrs(this as ComponentInstance, this as SetupContext)
updateVmAttrs(this as ComponentInstance)
},
updated(this: ComponentInstance) {
updateTemplateRef(this)
Expand Down
18 changes: 11 additions & 7 deletions src/utils/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,22 @@ export function updateTemplateRef(vm: ComponentInstance) {
vmStateManager.set(vm, 'refs', validNewKeys)
}

export function updateVmAttrs(vm: ComponentInstance, ctx: SetupContext) {
if (!vm || !ctx) {
export function updateVmAttrs(vm: ComponentInstance, ctx?: SetupContext) {
if (!vm) {
return
}
let attrBindings = vmStateManager.get(vm, 'attrBindings')
if (!attrBindings && !ctx) {
// fix 840
return
}
if (!attrBindings) {
const observedData = reactive({})
vmStateManager.set(vm, 'attrBindings', observedData)
attrBindings = observedData
attrBindings = { ctx: ctx!, data: observedData }
vmStateManager.set(vm, 'attrBindings', attrBindings)
proxy(ctx, 'attrs', {
get: () => {
return attrBindings
return attrBindings?.data
},
set() {
__DEV__ &&
Expand All @@ -128,8 +132,8 @@ export function updateVmAttrs(vm: ComponentInstance, ctx: SetupContext) {

const source = vm.$attrs
for (const attr of Object.keys(source)) {
if (!hasOwn(attrBindings!, attr)) {
proxy(attrBindings, attr, {
if (!hasOwn(attrBindings.data, attr)) {
proxy(attrBindings.data, attr, {
get: () => {
// to ensure it always return the latest value
return vm.$attrs[attr]
Expand Down
6 changes: 5 additions & 1 deletion src/utils/vmStateManager.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { ComponentInstance, Data } from '../component'
import { SetupContext } from '../runtimeContext'

export interface VfaState {
refs?: string[]
rawBindings?: Data
attrBindings?: Data
attrBindings?: {
ctx: SetupContext
data: Data
}
slots?: string[]
}

Expand Down
77 changes: 77 additions & 0 deletions test/setup.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1304,4 +1304,81 @@ describe('setup', () => {
lastName: 'xiao',
})
})

// #840
it('changing prop causes rerender to lose attributes', async () => {
let childAttrs = []
const Parent = {
computed: {
attrs() {
return {
'data-type': this.type,
}
},
},
props: {
type: {
type: String,
required: true,
},
},
mounted() {
childAttrs.push(this.attrs)
},
updated() {
childAttrs.push(this.attrs)
},
template: '<div v-bind="attrs">Parent<slot /></div>',
}
const Child = {
props: {
type: {
type: String,
required: true,
},
},
computed: {
attrs() {
return {
'data-type': this.type,
}
},
},
data() {
return {
update: 0,
}
},
template: ' <div v-bind="attrs">Child</div>',
}

const App = {
name: 'App',
data() {
return { parentType: 'parent' }
},
async mounted() {
await sleep(300)
this.parentType = 'parent-click'
},

components: {
Parent,
Child,
},
template: ` <div id="app">
<Parent :type="parentType">
<Child type="child"/>
</Parent>
</div>`,
}
const vm = new Vue(App).$mount()

await sleep(1000)
await vm.$nextTick()
expect(childAttrs).toStrictEqual([
{ 'data-type': 'parent' },
{ 'data-type': 'parent-click' },
])
})
})

0 comments on commit a43090d

Please sign in to comment.