N
Nuxt6mo ago
Gumaa

How to debug computed property

I have very weird case of "it is working, but I don't know why". And I don't know how to properly debug computed properties and find what exactly triggered them. It will be hard for me to provide a link for reproduction, but I will try to describe my setup in simplified terms. I have a component with a computed like this:
const { product } = toRefs(props);
const { isProductExcluded } = useProductExcluded();

const shouldShowProduct = computed(() => { // tiggered for seemingly no reason
console.log('recalculate computed');
return !isProductExcluded(product.value);
}
const { product } = toRefs(props);
const { isProductExcluded } = useProductExcluded();

const shouldShowProduct = computed(() => { // tiggered for seemingly no reason
console.log('recalculate computed');
return !isProductExcluded(product.value);
}
And I don't know why this computed is triggering, even though product.value is not changing. Somehow magically it knows when the return value of isProductExcluded is different and it is relaunching whole computed.
9 Replies
Gumaa
Gumaa6mo ago
This isProductExcluded function looks something like this (not exactly the same, but data flow is similar):
const { excludedRules } = useExcludedRules();

const ruleA = computed(() => excludedRules[0]);
const ruleB = computed(() => excludedRules[1]);
const ruleC = computed(() => excludedRules[2]);

const isProductExcluded = (product: Maybe<ProductVariant>) => {
return isConditionTrue(product, ruleA.value) ||
isConditionTrue(product, ruleB.value) || // this rule is changing
isConditionTrue(product, ruleC.value);
};
const { excludedRules } = useExcludedRules();

const ruleA = computed(() => excludedRules[0]);
const ruleB = computed(() => excludedRules[1]);
const ruleC = computed(() => excludedRules[2]);

const isProductExcluded = (product: Maybe<ProductVariant>) => {
return isConditionTrue(product, ruleA.value) ||
isConditionTrue(product, ruleB.value) || // this rule is changing
isConditionTrue(product, ruleC.value);
};
So I'm sending an async call to get the rules. Some time later I'm getting the response and I'm returning it as excludedRules. Then all the computed properties for rules are correctly recalculated. But at the point I got the response from the backend my component is already rendered and the function isProductExcluded already returned a value. product.value is already there and it is not changing. So even if now I have the needed data it shouldn't run. But it does! I even added artificial 10 second delay to the request to get excludedRules . Everything is rendered as it should, then data comes 10 seconds later, and it recalculates shouldShowProduct even when it should be impossible for Vue to do it, because this data is not a dependency of this computed. I'm questioning my sanity at that point a little bit. How do I debug this? How can I check what exactly is triggering this computed for shouldShowProduct? I don't think there is any way that Vue is somehow deeply checking dependencies of a function to check if computed needs rerunning? Or does it?
manniL
manniL6mo ago
@Gumaa check out onRenderTracked and onRenderTriggered
manniL
manniL6mo ago
Vue.js
Vue.js - The Progressive JavaScript Framework
manniL
manniL6mo ago
or for the specific computed onTrack / onTrigger
const plusOne = computed(() => count.value + 1, {
onTrack(e) {
// triggered when count.value is tracked as a dependency
debugger
},
onTrigger(e) {
// triggered when count.value is mutated
debugger
}
})
const plusOne = computed(() => count.value + 1, {
onTrack(e) {
// triggered when count.value is tracked as a dependency
debugger
},
onTrigger(e) {
// triggered when count.value is mutated
debugger
}
})
Gumaa
Gumaa6mo ago
Thanks for the response. Do you know if it is available for Vue 2? I'm checking right now docs for it but I can't find it I'm using Vue 2.6 with composition api package Ok, I've found that onRenderTracked and onRenderTriggered are not supported 😦 onTrack / onTrigger are also not available 😭
manniL
manniL6mo ago
ohh, for vue 2 yeah, bad luck with that 🙈 you could still watch all dependencies (with an immediate watcher) and check
Gumaa
Gumaa6mo ago
I mean the computed is pretty much only a call to this function !isProductExcluded(product.value), so I could try and setup a watcher just for product. I'm pretty sure it is not changing, but I will make sure with a watcher. Btw. how watcher being "immediate" helps in that scenario? I did something like this:
watch(product, (newValue, oldValue) => {
console.log({ oldValue, newValue });
}, { immediate: true });
watch(product, (newValue, oldValue) => {
console.log({ oldValue, newValue });
}, { immediate: true });
And it is printing when product has changed for the first time. But then after 10 seconds had passed and the request with the rules came back, it is not logging anything, and yet the computed just fires magically. So it really seems like it is deeply tracking dependecies of a function that it is calling.
Gumaa
Gumaa6mo ago
Ok, I was able to strip down everything and I think I have a representative minimal reproduction setup: https://stackblitz.com/edit/vue2-vue-cli-2ukgog?file=src%2FApp.vue,src%2Fmain.js I always believed that Vue does not track dependecies of a function like this. But it seems it does. I can not find any mention of this anywhere honestly.
StackBlitz
Vue2.6.14 + Vue CLI Starter + @vue/composition-api (forked) - Stack...
[vue2] Vue 2 SFCs with Vue CLI starter
Want results from more Discord servers?
Add your server