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

Cannot stop polling by setting refreshInterval 0 #632

Closed
SevenOutman opened this issue Sep 3, 2020 · 6 comments · Fixed by #638
Closed

Cannot stop polling by setting refreshInterval 0 #632

SevenOutman opened this issue Sep 3, 2020 · 6 comments · Fixed by #638

Comments

@SevenOutman
Copy link

SevenOutman commented Sep 3, 2020

SWR version 0.3.1

I have a demo reproducing this issue here.

In this demo I have a flag variable controlling whether swr should poll by conditionally setting refreshInterval value.

{
  refreshInterval: shouldPoll ? 1000 : 0
}

In my swr config I also have an onSuccess callback in which I change my flag value

{
  refreshInterval: shouldPoll ? 1000 : 0,
  onSuccess() {
    setShouldPoll(false);
  }
}

When the flag is set to false as the first fetch finishes, the polling stops normally as expected. Everything is fine so far.

However, if I set the flag value in the callback not of the first fetch but the revalidation fetches, polling is not stopping.

{
  refreshInterval: intValue < 2 ? 1000 : 0, // intValue is initially 0
  onSuccess() {
    setIntValue(value => value + 1); 
  }
}

After the first fetch, intValue is set to 1, and polling is supposed to continue. After the 2nd fetch, intValue is set to 2, and polling is supposed to stop because refreshInterval is now 0. But the polling continued.

@promer94
Copy link
Collaborator

promer94 commented Sep 7, 2020

useIsomorphicLayoutEffect(() => { 
   let timer = null 
   const tick = async () => { 
     if ( 
       !stateRef.current.error && 
       (config.refreshWhenHidden || isDocumentVisible()) && 
       (config.refreshWhenOffline || isOnline()) 
     ) { 
       // onSuccess called but revalidate has not finished yet, refreshInterval will trigger the clean-up function
       await revalidate({ dedupe: true }) 
     } 
     if (config.refreshInterval) {
        // timer changes after the clear-up function
       timer = setTimeout(tick, config.refreshInterval) 
     } 
   } 
   if (config.refreshInterval) { 
     timer = setTimeout(tick, config.refreshInterval) 
   } 
   return () => {
     if (timer) clearTimeout(timer) 
   } 
 }, [ 
   config.refreshInterval, 
   config.refreshWhenHidden, 
   config.refreshWhenOffline, 
   revalidate 
 ]) 
  • change the refreshInterval during the revalidate will trigger the clean-up function too quick.
  • the timer cant be update until the revalidate finished

@SevenOutman
Copy link
Author

@promer94 Is this expected behavior or an issue to be fixed?

@SevenOutman
Copy link
Author

I think it's the naming of onSuccess options that is tricky. I understand it as 'when fetches finish', while it's actually not.

A workaround can be moving my 'when fetches finish' logic from onSuccess into a customer fetcher.

async () => {
  await generalFetcher();
  setIntValue(value => value + 1); 
},
{
  refreshInterval: intValue < 2 ? 1000 : 0, // intValue is initially 0
  // onSuccess() {
  //   setIntValue(value => value + 1); 
  // }
}

@promer94
Copy link
Collaborator

promer94 commented Sep 8, 2020

swr/src/use-swr.ts

Lines 362 to 371 in ba18429

newData = await CONCURRENT_PROMISES[key]
setTimeout(() => {
delete CONCURRENT_PROMISES[key]
delete CONCURRENT_PROMISES_TS[key]
}, config.dedupingInterval)
// trigger the success event,
// only do this for the original request.
eventsRef.current.emit('onSuccess', newData, key, config)

onSuccess is actually 'when fetcher finish'.

@SevenOutman
Copy link
Author

@SevenOutman But you mentioned in your comment that "onSuccess called but revalidate has not finished yet"

@promer94
Copy link
Collaborator

promer94 commented Sep 8, 2020

A simplified revalidation steps

  1. revalidate start
  2. newData = await CONCURRENT_PROMISES[key]
    the fetcher has finished and we have newData
  3. eventsRef.current.emit('onSuccess', newData, key, config)
    onSucuess called
  4. newData is used to update stateRef.
  5. revalidate is over

So onSuccess is called after we successfully get the newData.

shuding pushed a commit that referenced this issue Sep 15, 2020
* fix Cannot stop polling by setting refreshInterval (#632)

* clear exiting the effect when key changes

* add test case for a-b-a and try fix a-b-a

* set less task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants