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

useQuery with fetchPolicy: 'network-only', returns stale cached results. #7058

Open
dmt0 opened this issue Sep 23, 2020 · 10 comments
Open

useQuery with fetchPolicy: 'network-only', returns stale cached results. #7058

dmt0 opened this issue Sep 23, 2020 · 10 comments

Comments

@dmt0
Copy link

dmt0 commented Sep 23, 2020

This is very similar to (or maybe the same as) #7048. I'm using useQuery with fetchPolicy: 'network-only'. In certain cases, it does not make a network request, and returns not just cached results, but stale cached results.

In my app I have asynchronous processes on backend, that take forever. After I do a mutation, I do an optimistic update with cache.modify in update function, and then startPolling on a query to continuously check if the operation is complete. This worked very well when I used Apollo V2. Not after the upgrade to V3.

Here's my custom hook that manages mutations and status checks:

const useItem = (someName) => {
  let variableToSync;

  const {data, loading, startPolling, stopPolling} = useQuery(
    GET_ITEMS,
    {
      variables: {name: someName},
      fetchPolicy: 'network-only',
      pollInterval,
    }
  );

  console.log('1 data', data?.apps?.apps[0]?.items);
  console.log(
    '1 cache',
    window.__APOLLO_CLIENT__.cache.data.data['SomeID']
      .items
  );

  const [addItem, {loading: adding}] = useMutation(
    ADD_ITEM,
    {
      variables: {
        someName: someName,
      },
      update: (cache, {data: res}) => {
        if (!res.addItem?.ok || res.addItem?.error) {
          return;
        }
    
        console.log(
          'cache before modify',
          window.__APOLLO_CLIENT__.cache.data.data['SomeID']
            .items
        );
        cache.modify({
          id: 'SomeID',
          fields: {
            items(existingRefs = []) {
              return [
                ...existingRefs,
                {
                  ...variableToSync,
                  status: res.addItem.refresh ? 'started' : '',
                  __typename: 'Item',
                },
              ];
            },
          },
        });
    
        if (res.addItem?.refresh) {
          console.log('startPolling');
          startPolling(pollInterval);
        }
    
        console.log(
          'cache after modify',
          window.__APOLLO_CLIENT__.cache.data.data['SomeID']
            .items
        );
      },
    }
  );

  console.log('2 data', adding, data?.apps?.apps[0]?.items);

  if (
    !adding &&
    data?.apps?.apps[0]?.items?.every(
      ({status}) => status !== 'started' && status !== 'queued'
    )
  ) {
    console.log('stopPolling');
    stopPolling();
  }

  return {
    add: (variable) => {
      variableToSync = variable;
      return addItem({variables: variable});
    },
    adding,
    items: (data?.apps?.apps[0]?.items || [])
      .map((e) => ({
        ...e,
        loading: e.status === 'started' || e.status === 'queued',
      }))
      .sort(sortBy('name')),
    loading,
  };
};

Intended outcome:

  1. Execute the query initially when loading the module
  2. User interaction prompts the mutation, which modifies the cache
  3. startPolling
    ...
  4. Hook start executing from the beginning, so the console.log's after the query should both print stuff that query pulled from backend.

Actual outcome:

  1. There is actually no network request coming from the query. Instead I see two things in console.log:

1 data (3) [{…}, {…}, {…}]
1 cache (4) [{…}, {…}, {…}, {…}]

So the cache actually did update correctly after cache.modify, but the data returned by useQuery returns some stale cache.

If I remove fetchPolicy: 'network-only', I get the correct cached result - which almost works for me, except if the above example gets extended to also do removeItem, and then a user starts to add and remove things quickly, things start getting out of sync and weird stuff happens. Again - this exact code (minus the API adjustments for V3) worked perfectly on V2.

How to reproduce the issue:

Above

Versions
System:
OS: macOS 10.15.6
Binaries:
Node: 12.13.1 - ~/.nvm/versions/node/v12.13.1/bin/node
Yarn: 1.22.5 - /usr/local/bin/yarn
npm: 6.12.1 - ~/.nvm/versions/node/v12.13.1/bin/npm
Browsers:
Chrome: 85.0.4183.121
npmPackages:
@apollo/client: ^3.2.0 => 3.2.0
apollo-link-logger: git://github.com/dmt0/apollo-link-logger.git#support-apollo-v3 => 0.0.0-development
apollo-upload-client: ^14.1.2 => 14.1.2
npmGlobalPackages:
apollo-link-logger: 0.0.0-development

@amineDaouma
Copy link

amineDaouma commented Sep 23, 2020

@benjamn @dmt0 I think that we face almost the same issue with the stale data in cache when we make a Query with fetchPolicy='network-only'. In my example : https://github.com/apollographql/apollo-client/issues/7048 I see a network request made to the server however the data returned by the GraphQL server is not rendered by AC3 and instead Apollo Client cache pulls data from the cache and ignores backend returned data. As if Apollo Client v3 does something to prevent cache overwriting because normally when you specify fetchPolicy='network-only' AC3 should then update the cache with backend data and does this automatically by matching the ID coming from the server and the ID of the object to be modified in cache. I think that maybe there could be another step to do in order to update the existing data in cache .. This stuff works well with Apollo Client 2 (with returning ID) and now it seems that it is broken. I'm blocked right now :/

@magicmark
Copy link
Contributor

Similar to #7045 (comment) ?

@dmt0
Copy link
Author

dmt0 commented Sep 24, 2020

@magicmark I've seen your issue too. Doesn't seem like it's the same, I didn't have a network error.

@dmt0
Copy link
Author

dmt0 commented Mar 26, 2021

This bug is still there as of v3.4.0-beta.18

@FedericoDiRosa
Copy link

I am also experiencing a similar (maybe the same?) issue @dmt0
In my case is when I call refetch returned from useQuery (which goes out with a network-only fetch policy see docs).
The data is stale while refetch is pending. I can mutate the cache but the change is not reflected in the component until refetch resolves.
My workaround for now is to fire a custom refetch, which is nothing more than another query with same variables, using client.query returned from useQuery

I am on v.3.5.9

@FedericoDiRosa
Copy link

FedericoDiRosa commented Mar 22, 2022

And here's a simplified version in a sandbox https://codesandbox.io/s/refetch-cache-update-fail-k5s7x6?file=/src/exchange-rates.js

@jsduffy
Copy link

jsduffy commented Oct 31, 2022

+1

@Oleh-Khozhai
Copy link

+1

1 similar comment
@AM-77
Copy link

AM-77 commented Aug 21, 2023

+1

@DogAndHerDude
Copy link

Yup, can confirm. This bug shows up on 3.8.2 still. Odd thing I have two similar environments that use different queries but generally work the same way, and one does not encounter this issue while the other using network-only or anything else other than no-cache will return stale data given new variables.

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

No branches or pull requests

9 participants