I have an object ...
export class Note {
type = ''
id: string = v4()
creationDate: string = DateTime.now().toISO()
modificationDate: string = DateTime.now().toISO()
text = ''
title = ''
tags: string[] = []
deleted = false
deletedAt?: string = ''
erased = false
erasedAt?: string = ''
archived = false
archivedAt?: string = ''
deleteOn?: string = ''
pinned = false
// methods follow
}
... that I pass as a prop to a component
<script lang='ts' setup>
const props = defineProps({
currentNote: {type: Object, default: () => {return {}}},
})
const note = toRef(props, 'currentNote') as Ref<Note>
const toWatch = computed(() => [toRef(note, 'title'), toRef(note, 'text'), toRef(note, 'tags'), toRef(note, 'type')])
watch(toWatch, debounce(() => {console.log(JSON.stringify(note)); note.value.writeToOffline()}, 1000), {deep: true})
(...)
What happens, however, is that the watch
callback gets triggered whenever any property of note
changes.
The construction above was to only watch selected properties, but apparently they carry any change.
What is wrong with the logic in my code?
CodePudding user response:
Not reproducible. Check demo below...
- Changes to
archived
do not trigger watcher - Changes to
title
also do not trigger watcher - note the code insidetoWatch
computed - the code is same as in your example:Vue.toRef(note, 'title')
- Changes to
text
triggers the watcher - To fix problem above, you need to useVue.toRef(note.value, 'text')
eg. to pass a ref value instead of ref itself totoRefs
All this works without {deep: true}
because watch
can watch an array of refs (and as it seems also ref
containing array of refs
)
class Note {
constructor() {
this.type = '';
this.id = 'asdsad';
this.text = 'text';
this.title = 'title';
this.tags = [];
this.deleted = false;
this.deletedAt = '';
this.erased = false;
this.erasedAt = '';
this.archived = false;
this.archivedAt = '';
this.deleteOn = '';
this.pinned = false;
}
}
const app = Vue.createApp({
setup(){
const note = Vue.reactive(new Note())
const modifyArchived = function() {
note.archived = !note.archived
}
const modifyTitle = function() {
note.title = note.title ' '
}
const modifyText = function() {
note.text = note.text ' '
}
return {
note,
modifyArchived,
modifyTitle,
modifyText
}
},
})
app.component('child', {
props: ['currentNote'],
setup(props) {
const note = Vue.toRef(props, 'currentNote')
const changes = Vue.ref(0)
const toWatch = Vue.computed(() => [Vue.toRef(note, 'title'), Vue.toRef(note.value, 'text'), Vue.toRef(note, 'tags'), Vue.toRef(note, 'type')])
Vue.watch(toWatch, () => { changes.value } /*, {deep: true} */)
return {
note,
changes
}
},
template: `
<div>Child: (changes = {{ changes }})</div>
<div>archived: {{ note.archived }}</div>
<div>title: {{ note.title }}</div>
<div>text: {{ note.text }}</div>
`
})
app.mount('#app')
<script src="https://unpkg.com/[email protected]/dist/vue.global.js"></script>
<div id='app'>
<div>Parent:</div>
<div><button @click="modifyArchived">Modify archived (no trigger)</button></div>
<div><button @click="modifyTitle">Modify title (no trigger - not ok!)</button></div>
<div><button @click="modifyText">Modify text (trigger - ok!)</button></div>
<child :current-note="note"></child>
</div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>