apollo-client icon indicating copy to clipboard operation
apollo-client copied to clipboard

Using useQuery with pollInterval triggers onCompleted only once

Open kocur4d opened this issue 6 years ago • 42 comments

Intended outcome:

I would expect the onCompleted callback to be fired after every poll.

Actual outcome:

onCompleted is being fired only once. First time the query is made.

How to reproduce the issue:

const { data, stopPolling } = useQuery(QUERY, {
  variables: {...},
  fetchPolicy: 'network-only',
  pollInterval: 1000,
  onCompleted: () => console.log('called')
})

Versions

npmPackages: @apollo/react-common: ^3.0.1 => 3.0.1 @apollo/react-hooks: ^3.0.1 => 3.0.1 apollo-cache-inmemory: ^1.3.5 => 1.3.11 apollo-client: ^2.6.4 => 2.6.4 apollo-link: ^1.2.3 => 1.2.4 apollo-link-context: ^1.0.10 => 1.0.10 apollo-link-error: ^1.1.1 => 1.1.2 apollo-link-http: ^1.5.5 => 1.5.7 apollo-link-logger: ^1.2.3 => 1.2.3 apollo-server-koa: ^2.1.0 => 2.2.4 apollo-utilities: ^1.3.2 => 1.3.2 react-apollo: ^2.2.4 => 2.3.2

kocur4d avatar Oct 31 '19 12:10 kocur4d

Have you checked the network traffic? I believe that it does not actually poll, as opposed to executing onComplete only on the first query.

ajhool avatar Nov 07 '19 22:11 ajhool

@ajhool I have just tested it again and I can confirm that the query is being called multiple times. I can see it in the network traffic every second, but the onCompleted is being called only once.

kocur4d avatar Nov 12 '19 10:11 kocur4d

Yeah i'm seeing the same thing.

        "apollo-cache": "^1.3.2",
        "apollo-cache-inmemory": "^1.6.3",
        "apollo-client": "^2.6.4",
        "apollo-link": "^1.0.6",
        "apollo-link-error": "^1.0.3",
        "apollo-link-http": "^1.3.1",
        "graphql-tag": "^2.4.2",
        "ts-invariant": "^0.4.0",
        "tslib": "^1.9.3"

jaredmdobson avatar Nov 21 '19 16:11 jaredmdobson

+1 Having same problem here. onCompleted will be fired only once at the first fetch.

ScripterSugar avatar Nov 21 '19 17:11 ScripterSugar

+1 Im also seeing the same issue

maddog986 avatar Dec 20 '19 03:12 maddog986

Setting notifyOnNetworkStatusChange to true solved the issue in my case.

mamousavi avatar Dec 22 '19 06:12 mamousavi

This could be the culprit?

https://github.com/apollographql/apollo-client/blob/6bc9fdcfc064bd60533794f0ef5aeed45f7ad537/src/react/data/QueryData.ts#L433

It seems onCompleted only runs when data has changed, so if you are polling and no data changes occur then it will not fire.

Ideally there could be an extra prop to always call. Or maybe an alternative function prop, onCompletedAlways?

bhishp avatar Jan 31 '20 11:01 bhishp

A workaround for our case was to add fetchPolicy: 'no-cache', to the query options. Thus:

const { data, stopPolling } = useQuery(QUERY, {
  variables: {...},
  fetchPolicy: 'network-only',
  pollInterval: 1000,
  onCompleted: () => console.log('called'),
  fetchPolicy: 'no-cache',
})

Obviously this means you will bypass the client-side cache but it will ensure the completion hook is triggered every time.

bhishp avatar Jan 31 '20 16:01 bhishp

We've tried the workarounds here but none work.

We don't see onCompleted firing at all with @apollo/client 3.0.0-beta.37. Even the first call to the query doesn't print to the console.

  const messagesQuery = useQuery(GET_CHAT_MESSAGES_BY_GROUP_ID, {
    variables: { chatGroupId },
    pollInterval: 1000,
    onCompleted: () => console.log('If this worked no useEffect needed. 😕'),
  });

GollyJer avatar Feb 27 '20 20:02 GollyJer

I'm running into this issue as well. Even with fetchPolicy: 'no-cache' the onCompleted handler is only being called once.

jcelliott avatar Mar 30 '20 16:03 jcelliott

The same is true for onError. It's called for the first error but is not called if later poll attempts have errors. The workarounds do not help me.

marcdavi-es avatar Apr 06 '20 13:04 marcdavi-es

Can confirm as well

3nvi avatar Apr 17 '20 15:04 3nvi

Same behaviour here

andreasonny83 avatar Apr 29 '20 15:04 andreasonny83

Guys, set the fetchPolicy: 'network-only', it should work then, I had the same problem. And better switch to: useLazyQuery instead of polling if it is possible.

dominik-myszkowski avatar Apr 29 '20 16:04 dominik-myszkowski

Guys, set the fetchPolicy: 'network-only', it should work then, I had the same problem. And better switch to: useLazyQuery instead of polling if it is possible.

Erm sorry but that's subjective. I need to store it in the cache since it's very expensive for me to re-fetch it. I need polling since I'm using it for an async server operation that has the results ready in between 30secs and 2 minutes, thus I need to continuously "check" if they are ready for serving

3nvi avatar Apr 29 '20 16:04 3nvi

Also, @dominik-myszkowski , setting fetchPolicy: 'network-only' only triggers the onCompleted once. I can only get it working by setting notifyOnNetworkStatusChange: true

andreasonny83 avatar Apr 29 '20 16:04 andreasonny83

This could be the culprit?

https://github.com/apollographql/apollo-client/blob/6bc9fdcfc064bd60533794f0ef5aeed45f7ad537/src/react/data/QueryData.ts#L433

It seems onCompleted only runs when data has changed, so if you are polling and no data changes occur then it will not fire.

Ideally there could be an extra prop to always call. Or maybe an alternative function prop, onCompletedAlways?

Are any of you falling in to this conditional? Is it not firing when no data has changed?

bhishp avatar Apr 29 '20 17:04 bhishp

@bhishp I would really love the onCompeted event only to run on data changed. however even when the data changes, I cannot see the onCompeted method triggered

andreasonny83 avatar Apr 29 '20 17:04 andreasonny83

Setting notifyOnNetworkStatusChange to true solved the issue in my case.

This worked for me. Setting 'network-only' did not.

krishnaku avatar May 02 '20 13:05 krishnaku

@hwillson any updates on that with the release of 3.0?

3nvi avatar Jul 16 '20 19:07 3nvi

@andreasonny83 I'm seeing the same, the data changed but my onComplete didn't fire. notifyOnNetworkStatusChange fixed the issue for me. Still happening even in version 3.1.0-pre.1

martdavidson avatar Jul 24 '20 14:07 martdavidson

The issue with setting notifyOnNetworkStatusChange is that it will rerender for every poll interval (as documented). This is probably not what you want and you'd want to rerender only when the data changes.

jure avatar Jul 28 '20 13:07 jure

@jure Yes, but as @andreasonny83 mentioned and I've also confirmed, it isn't firing when the data changes.

martdavidson avatar Jul 28 '20 13:07 martdavidson

Right, absolutely, it should! That's the bug. I've commented merely to point out that setting notifyOnNetworkStatusChange isn't a workable workaround in many situations and that folks should be aware of that drawback before applying it willy nilly, as it causes the whole tree below the hook to rerender on every interval.

jure avatar Jul 28 '20 13:07 jure

For what it's worth, I've sort of resolved the issue caused by this workaround for the time being by chucking the polling useQuery into a dead end of the tree, so the rerendering isn't annoying. In there I then use makeVar which is then used in the typePolicies of the InMemoryCache, like this:

  {
    cache: new InMemoryCache({
      typePolicies: {
        Manuscript: {
          fields: {
            _currentRoles: {
              read(existing, { cache, args, readField }) {
                const currentRoles = currentRolesVar()
              },
            },
          },
        },
      },
    }

It's quite the detour, but it works, so hopefully it's useful for someone else too.

jure avatar Jul 28 '20 13:07 jure

Yup, can confirm. Still happening in 3.3.6. I resolved the issue with notifyOnNetworkStatusChange , though it's like using a baseball bat to clean the dishes.

The only other alternative I can come up with is using a useEffect, and refetch.

DogAndHerDude avatar Jan 06 '21 09:01 DogAndHerDude

This has proven to be the most reliable combination for us. note the separate user queries and the manual starting and stoping of polling. this bypasses a second bug in which stop polling does not reliably work with pollinterval

  const [completed,setCompleted] = useState(false);

  const { data: versionData, loading: versionLoading, stopPolling, startPolling } = useQuery(QUERY, {
    variables: {
      id: id,
    },
  })

useEffect(() => {
    if (versionData) {
        ...
    }, [versionData])

useEffect(() => {
    // versionRefetch()
    if (!completed) {
      startPolling(4000)
    } else {
      stopPolling()
    }
    return () => {
      stopPolling()
    }
  }, [stopPolling, startPolling, completed])
  

doflo-dfa avatar Apr 10 '21 01:04 doflo-dfa

I created a runnable reproduction of this issue (use branch polling-oncompleted-not-called): https://github.com/dylanwulf/react-apollo-error-template/tree/polling-oncompleted-not-called

dylanwulf avatar Apr 15 '21 18:04 dylanwulf

This should now be resolved in @apollo/[email protected]. Let us know if you notice any issues. Thanks!

hwillson avatar Nov 08 '21 21:11 hwillson

I don't think this is fixed @hwillson

Here's a codesandbox repro. Making notifyOnNetworkStatusChange: true fixes it (as it always did), but when it's false, the original issue still exists.

Should this be re-opened?

3nvi avatar Dec 08 '21 20:12 3nvi