Skip to content
This repository has been archived by the owner on Apr 13, 2023. It is now read-only.

Infinite render when useQuery defines onCompleted with 'cache-and-network' fetchPolicy #4008

Open
johnnyoshika opened this issue Jun 5, 2020 · 12 comments

Comments

@johnnyoshika
Copy link

Intended outcome:

When using useQuery with onCompleted and fetchPolicy set to cache-and-network, I expected the network request to be triggered only once.

Actual outcome:

Resulted in an infinite render and infinite GraphQL requests.

Sample code:

  const { data } = useQuery(SOME_QUERY, {
    onCompleted: () => {},
    fetchPolicy: "cache-and-network"
  });

How to reproduce the issue:

Bug can be reproduced here: https://codesandbox.io/s/continents-h0822

Specifically in this file: https://codesandbox.io/s/continents-h0822?file=/src/Continents.js

The console will show this and execute 4 GraphQL requests before execution will be forced to stop:

render 1
render 2
render 3
render 4
render 5
render 6

Codesandbox's page can be run here: https://h0822.csb.app/

Version

  System:
    OS: Windows 10 10.0.18363
  Binaries:
    Node: 12.16.1 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.4 - ~\scoop\apps\yarn\current\Yarn\bin\yarn.CMD
    npm: 6.13.4 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: 0.00
  npmPackages:
    @apollo/client: ^3.0.0-rc.2 => 3.0.0-rc.2

Also reproduced in codesandbox: https://codesandbox.io/s/continents-h0822

@johnnyoshika
Copy link
Author

This could be somewhat related: #3333

@thomassuckow
Copy link

thomassuckow commented Jun 5, 2020

@apollo/client 3.0.0-rc.0 Changed the behaviour of onCompleted so it must be memoized in the component. We are seeing crazy infinite behavior and are trying to figure out a way in @apollo/react-hoc to deal with that.

The answer is probably just to port all the files that do it. Otherwise crazy fixes would be needed.

apollographql/apollo-client#6301

In the meantime we went back to beta 45

@johnnyoshika
Copy link
Author

@thomassuckow Oh, good to know that I can go back to beta 45 to get around this problem for now.

@johnnyoshika
Copy link
Author

I quickly switched this sample app to use beta 45 and everything worked as expected: https://codesandbox.io/s/continents-h0822

So it is indeed a problem that was introduced after that release.

@thomassuckow
Copy link

thomassuckow commented Jun 8, 2020

I believe they are intending to keep it this way, which I can understand as it probably fixes a lot of edge cases. But it doesn't interact so well with react-hoc

@johnnyoshika
Copy link
Author

@thomassuckow They're intending to keep the infinite loop??? Is that a strange decision?

@thomassuckow
Copy link

thomassuckow commented Jun 8, 2020

They are intending to keep the requirement that onComplete, et. al needs to be memoized. Since Apollo Client 3 is react hook based, that requirement makes sense (Rules of Hooks). The problem is how the method was typically provided to the HOC does not lend itself to memoization.

@johnnyoshika
Copy link
Author

@thomassuckow Interesting. Is there documentation whereonCompleted and memoization is discussed? Can you point me to some sample code where a callback like that is memoized? Thanks in advance.

@thomassuckow
Copy link

I cannot for the life of me find what I swear was a dev or a commit stating they intentionally changed the behavior.

Regardless, When using useQuery the options object has onComplete. Anywhere that is used, the callback would need to be wrapped in a useCallback.

const f = useQuery(FOO, { onComplete: () => { doSomething(); doSomethingElse() } });

becomes

const callback = useCallback(() => { doSomething(); doSomethingElse() }, []);
const f = useQuery(FOO, { onComplete: callback });

@johnnyoshika
Copy link
Author

@thomassuckow that works perfectly, thank you! So if I understand correctly, useQuery re-requests the GraphQL request if any of the paramaters are different (using shallow comparison, I assume).

@thomassuckow
Copy link

Yep. Which when the cache strategy is set to network-only causes an infinite loop if one of them isn't memoized.

@millievn
Copy link

I have two functions but same lazyloadquery in one page, one for loading by pagination and another for loading all data. Like this:

function useSomeBooksApi(){
  const [fetch,{loading,data}] = useBooksLazyQuery({fetchPolicy: 'cache-and-network'})
  const load = (groupId,page,size)=>{
     fetch({variables:{groupId,page,size}})
  }

 return {
  load,data,loading
 }
}

function useAllBooksApi(){
  const [fetch,{loading,data}] = useBooksLazyQuery({
     fetchPolicy: 'network-only', // or no-cache
     onCompleted(data){
       downloadCsv(data.books)  
    }
  })

 return {
  fetch,data,loading
 }
}

When I use those in one page, useBooksLazyQuery calls infinitely. But different lazyloadquery with same usage works fine.

Info:

  • "@apollo/react-hooks": "^3.1.5"
  • "apollo-cache-inmemory": "^1.6.6"
  • "apollo-client": "^2.6.10"
  • "@graphql-codegen/cli": "^1.15.4"
  • "graphql": "^15.1.0"

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants