Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

watch onInvalidate callback is triggered many times #5151

Closed
edikdeisling opened this issue Dec 22, 2021 · 4 comments
Closed

watch onInvalidate callback is triggered many times #5151

edikdeisling opened this issue Dec 22, 2021 · 4 comments
Labels
has workaround A workaround has been found to avoid the problem scope: reactivity

Comments

@edikdeisling
Copy link

Version

3.2.26

Reproduction link

sfc.vuejs.org/

Steps to reproduce

  1. Click on button multiple times
  2. 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
image


There is enchancement issue that would fix this behaviour, I think.
#3341

@LinusBorg
Copy link
Member

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();
  });
});

@posva posva added the has workaround A workaround has been found to avoid the problem label Dec 22, 2021
@edikdeisling
Copy link
Author

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...

@cyrilluce
Copy link
Contributor

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 #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();
  });
});

@github-actions github-actions bot locked and limited conversation to collaborators Nov 25, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
has workaround A workaround has been found to avoid the problem scope: reactivity
Projects
None yet
Development

No branches or pull requests

5 participants
@posva @LinusBorg @cyrilluce @edikdeisling and others