watch onInvalidate callback is triggered many times
Version
3.2.26
Reproduction link
Steps to reproduce
- Click on button multiple times
- Watch the console output
What is expected?
Callback that we pass to onInvalidate calls only once. In reproduction repo I expect that abort
would be called the same times as fetch
const count = ref(0);
watch(count, (value, oldValue, onInvalidate) => {
if (value > 2) {
return;
}
const controller = new AbortController();
// fetch would be called only 2 times
console.log('fetch!');
fetch('/test', { signal: controller.signal });
onInvalidate(() => {
// but onInvalidate would be calld every time watch triggers
console.log('aborted!');
controller.abort();
});
});
What is actually happening?
Callback that we pass to onInvalidate is called multiple times
In the screenshot fetch was called 2 times, but abort was called 18 times
There is enchancement issue that would fix this behaviour, I think. https://github.com/vuejs/vue-next/issues/3341
This is a tricky one, feels like a definition gap. If we change the behavior now, we risk breaking existing code. We might still want to if we decide the proposed behavior was the originally intended one anyway.
For now, one could work around this like so:
const count = ref(0);
watch(count, (value, oldValue, onInvalidate) => {
if (value > 2) {
return;
}
const controller = new AbortController();
// fetch would be called only 2 times
console.log('fetch!');
fetch('/test', { signal: controller.signal });
let invalidated = false
onInvalidate(() => {
if (invalidated) return
invalidated = true
console.log('aborted!');
controller.abort();
});
});
Thanks for work around and answer! For me "onInvalidate" means callback that fired when "valid" state changes to "invalid". Right now this callback may be called even when "state" has already "invalid". This cause some weirdness. But yeah, the existing code matters here...
Another work around
BTW, this behavior is werid. I'm using vue 3, and thought onCleanup(disposer)
is equal to React's return disposer
, and even should accept multiple disposers, see https://github.com/vuejs/core/issues/3341
const count = ref(0);
watch(count, (value, oldValue, onInvalidate) => {
// ------ Add this line ------
onInvalidate(()=>{})
// ---------------------------
if (value > 2) {
return;
}
const controller = new AbortController();
// fetch would be called only 2 times
console.log('fetch!');
fetch('/test', { signal: controller.signal });
onInvalidate(() => {
// but onInvalidate would be calld every time watch triggers
console.log('aborted!');
controller.abort();
});
});