apollographql / apollo-client

:rocket:  A fully-featured, production ready caching GraphQL client for every UI framework and GraphQL server.
https://apollographql.com/client
MIT License
19.37k stars 2.66k forks source link

Inconsistent cache result's when two or more component calls to useLazyQuery with the same query #7923

Open DaniAcu opened 3 years ago

DaniAcu commented 3 years ago

Intended outcome: The cache should be shared between two instances of useLazyQuery that use the exact same parameters. If we create an abstraction of useLazyQuery each component that uses it should have the same expected behavior (fetching to graphql or returning cache results).

Abstraction sample

function useFetchBooks () {
   const [, setState] = useGlobalState();

   const [fetchBooks, { data, loading, error }] = useLazyQuery(SIMPLE_QUERY);

   useEffect(() => {
     if(loading || !data) return;

     setState({
       books: data.results
     })
   }, [data, loading])

  return React.useCallback(
    (varaibles) => {
      fetchBooks({
        variables
      });
    },
    [fetchBooks]
  );
}

Actual outcome: If you use the abstraction into 2 components and show the store list into another component. You will have mismatched between the results. Every time that call to graphql will work but when you try to load the result from cached 2 times it will return all time the first

How to reproduce the issue: Steps to reproduce it:

Codesandbox: https://codesandbox.io/s/uselazyqueryissue-forked-1wv4c?file=/src/App.jsx

I create a small store using React.Context and try to save on the store each result. And an effect to watch for data changes because onComplete doesn't dispatch on cache results

Versions

System:
    OS: Linux 4.4 Debian GNU/Linux 10 (buster) 10 (buster)
Binaries:
    Node: 12.18.2 - ~/.nvm/versions/node/v12.18.2/bin/node
    Yarn: 1.22.5 - /usr/bin/yarn
    npm: 6.14.8 - /usr/bin/npm
Browsers:
    Firefox: 78.5.0esr
npmPackages:
    @apollo/react-testing: 3.1.4 => 3.1.4 
    apollo-boost: 0.4.9 => 0.4.9 
    apollo-cache-inmemory: 1.6.6 => 1.6.6 
    apollo-link-batch-http: 1.2.14 => 1.2.14 
    apollo-link-timeout: 1.4.1 => 1.4.1 
    apollo-progressive-fragment-matcher: 1.0.8 => 1.0.8 
    react-apollo: 3.1.5 => 3.1.5 
brainkim commented 3 years ago

Hi! Thank you for opening this issue, and thank you for providing a runnable code example.

Unfortunately, I’m having a bit of trouble wrapping my head around your code. Firstly, you’re writing an onSubmit handler but passing it as an onChange callback, which in essence causes the callback to fire whenever the user types, rather than when the form is submitted. Therefore, you’re actually causing a form submit and reloading the page whenever you click one of those buttons. Secondly, I’m having a little bit of difficulty attempting to understand what you intend to happen because the heavy usage of contexts. I count three contexts in the example alone, and not sure what passing around the setState/reducer functions via contexts is meant to accomplish.

Finally, can you rephrase this part:

If you use the abstraction into 2 components and show the store list into another component. You will have mismatched between the results. Every time that call to graphql will work but when you try to load the result from cached 2 times it will return all time the first

What do you expect to see on the screen? Do you want the queries to run independently? Do you expect the lists to be concatenated? I might need a little bit of help understanding this part, and am very sorry if I’m missing the point of your example 😓

As always, thanks for reaching out, and I’m happy to go back and forth until we figure this out. I’ll leave your code on my computer for now so I can jump back in and try things out when you get a chance to respond 😊

DaniAcu commented 3 years ago

First of all, sorry. I played around with it to check how I can fixed in my proyect. Let me create a fork just with the error to prevent misunderstanding. https://codesandbox.io/s/uselazyqueryissue-forked-1wv4c?file=/src/App.jsx

In simple words, the issue is that the cache is not updating. If you use the abstraction that I put abov into 2 component at the same level as the ActionSection and follow the steps. The last submit is dispatched but doesn't update the list (the data is not updating with the cache).

I was create other component and a hook (Wrapper and useApplyWrapper) that only put this on top of the tree and share the exactly same function to each component that case, solve the error.

Gif: useLazyIssue