Reputation: 4908
I am trying to (deep) watch for any changes on a reactive object in Vue 3:
import { reactive, watchEffect } from 'vue';
const settings = reactive({
panes: {
left: {
defaultAction: "openInOtherPane",
},
right: {
defaultAction: "openInThisPane",
},
},
showMenu: false,
isDrawerOpen: false,
});
watchEffect(() => {
console.log('The new settings are', settings);
});
// Try to change a setting (this does not trigger watchEffect)
settings.panes.left.defaultAction = "none";
This code has two problems:
Proxy { <target>: {…}, <handler>: {…} }
I also tried watch
:
watch(
() => settings,
settings => {
console.log('The new settings are', settings);
},
);
With the same problems.
When I only watch a property on the object, then it does work:
watchEffect(() => {
console.log('Is the menu shown:', settings.showMenu); // This works
});
And with destructuring I can watch changes one level deep, but not more levels:
watchEffect(() => {
// This works one level deep
console.log('Settings are changed:', { ...settings });
});
The settings can be logged using toRaw, but then the watchEffect is not triggered:
import { toRaw, watchEffect } from 'vue';
watchEffect(() => {
// console.log shows the raw object, but watchEffect is not triggered
console.log('Settings are changed:', toRaw(settings) );
});
Is there an elegant method to watch for changes on properties any level deep in a reactive object?
Upvotes: 7
Views: 12080
Reputation: 4908
After some better reading of documentation, I found that you can give watch()
the option { deep: true }
like this:
import { toRaw, watch } from 'vue';
watch(
() => settings,
settings => {
// use toRaw here to get a readable console.log result
console.log('settings have changed', toRaw(settings));
},
{ deep: true },
)
watchEffect cannot be used in this way, because watchEffect is triggered by references to reactive properties, and you would have to loop through the whole object and do something with al the values to trigger it.
watch()
with { deep: true }
seems to be the best option.
Upvotes: 16
Reputation: 396
If it is enough to watch for changes in the first level properties of an object, this worked surprisingly well for me:
watchEffect(() => {
toRefs(objectToWatch)
// perform some side effect
})
Upvotes: 3
Reputation: 2619
Watching Reactive Objects
Using a watcher to compare values of an array or object that are reactive requires that it has a copy made of just the values.
const numbers = reactive([1, 2, 3, 4])
watch(
() => [...numbers],
(numbers, prevNumbers) => {
console.log(numbers, prevNumbers);
})
numbers.push(5) // logs: [1,2,3,4,5] [1,2,3,4]
Upvotes: 0