question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

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

See original GitHub issue

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

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:3
  • Comments:7 (1 by maintainers)

github_iconTop GitHub Comments

2reactions
amineDaoumacommented, 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 😕

0reactions
jsduffycommented, Oct 31, 2022

+1

Read more comments on GitHub >

github_iconTop Results From Across the Web

Queries - Apollo GraphQL Docs
If all data is available locally, useQuery returns that data and doesn't query your GraphQL server. This cache-first policy is Apollo Client's default...
Read more >
Fetching data with React - GQty
This hook returns the core query object, and it will detect the usage of it, and suspend ... import { Suspense } from...
Read more >
useQuery data object not updating with cache : r/graphql
Like the title says, I am using useQuery to return a data object, ... This also happens when I set the fetchPolicy to...
Read more >
Fetch Policies | Relay
The first step to reusing locally cached data is to pass a fetchPolicy to the ... the local cache; if any piece of...
Read more >
Caching Examples | TanStack Query Docs
Let's assume we are using the default cacheTime of 5 minutes and the default staleTime of 0 . A new instance of useQuery({...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found