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

Using pagination with fetchMore and fetchPolicy involving network together causes bug where next page data is cleared #6327

Closed
joonhocho opened this issue May 21, 2020 · 21 comments

Comments

@joonhocho
Copy link

joonhocho commented May 21, 2020

Intended outcome:
fetchMore and updateQuery for pagination via useQuery should work well without a bug with fetchPolicy that involves network such as 'network-only', 'cache-and-network', etc.

Actual outcome:
fetchMore and updateQuery works, but updateQuery immediately triggers apollo's query observables to refetch query with old variables (with pagination cursor = null).
Refetched data (where cursor = null) overwrites result from updateQuery causing new page data to be appended and then immediately removed because of the overwrite.
This clearly seems like a bug of apollo client.

How to reproduce the issue:
Implement useQuery pagination with cursor and updateQuery from official example and use fetchPolicy: "cache-and-network" or "network-only".

Versions
System:
OS: macOS 10.15.3
Binaries:
Node: 13.13.0 - /usr/local/bin/node
Yarn: 1.22.4 - /usr/local/bin/yarn
npm: 6.14.4 - /usr/local/bin/npm
Browsers:
Chrome: 81.0.4044.138
Firefox: 75.0
Safari: 13.0.5
npmPackages:
@apollo/client: ^3.0.0-beta.49 => 3.0.0-beta.49
@apollo/link-context: ^2.0.0-beta.3 => 2.0.0-beta.3
@apollo/link-error: ^2.0.0-beta.3 => 2.0.0-beta.3
@apollo/link-persisted-queries: ^1.0.0-beta.0 => 1.0.0-beta.0

@vishwam
Copy link
Contributor

vishwam commented May 22, 2020

I would also like to some resolution to this, but I recognize this is not an easy thing to implement. I have a similar scenario with a paginated list and a cache-and-network policy. When the user first visits the list and starts scrolling down, the list query starts accumulating the full results.

When the user goes away and comes back to the list later, they initially see the full list of items. The network response them comes back and removes everything except the first page.

This is something that is typically solved with http etag headers (to trigger optimizations on both the client and server) but I’m not sure what the graphql equivalent is.

@anark
Copy link
Contributor

anark commented May 24, 2020

I am also having this issue using version 3.0.0-beta.50.

Basically if I do my query as follows it works great

function ItemsList({ scopeId }) {
  const [loadingMore, setLoadingMore] = useState(false)
  const GET_ITEMS = gql`
    query Items($scopeId: ID!, $cursor: String) {
      items(scopeId: $scopeId, first: 10, after: $cursor) {
        pageInfo {
          hasNextPage
          endCursor
        }
        nodes {
          .id
           title
        }
      }
    }
  `

  const { data, loading, error, fetchMore } = useQuery(GET_ITEMS, {
    variables: { scopeId }
  })

  if (loading) return <p>Loading...</p>
  if (error) return <p>Error</p>
  return (
    <>
      {data.items.nodes.map((item) => (
        <p key={item.id}>{item.title}</p>
      ))}

      {data?.items?.pageInfo.hasNextPage && (
        <Button
          disabled={loadingMore}
          onClick={() => {
            setLoadingMore(true)
            fetchMore({
              variables: { cursor: data.items.pageInfo.endCursor },
              updateQuery: (previousResult, { fetchMoreResult }) => {
                setLoadingMore(false)
                if (!fetchMoreResult) return previousResult
                const newNodes = fetchMoreResult.items.nodes
                const pageInfo = fetchMoreResult.items.pageInfo
                console.log([...previousResult.items.nodes, ...newNodes])
                return {
                  ...previousResult,
                  items: {
                    nodes: [...previousResult.items.nodes, ...newNodes],
                    pageInfo
                  }
                }
              }
            })
          }}
        >
          {loadingMore ? 'Loading more...' : 'Load More'}
        </Button>
      )}
    </>
  )
}

However if I add a fetchPolicy of 'cache-and-network' to the useQuery

  const { data, loading, error, fetchMore } = useQuery(GET_ITEMS, {
    variables: { scopeId },
    fetchPolicy: 'cache-and-network'
  })

The console.log will show the correct output, however when the component updates it will only have the original data and none of the new data.

I am using a loadingMore scope here because I couldn't bet the loading variable to update when fetchMore was being called regardless of the notifyOnNetworkStatusChange.

@fdev
Copy link

fdev commented May 25, 2020

It's not just fetchMore, subscribeToMore has the same problem (#6340).

Edit: #6305 looks awfully similar as well.

@yamalight
Copy link

Having the same issue.
Here's the simplest repro on codesandbox

@benjamn benjamn added this to the Release 3.0 milestone May 26, 2020
@hwillson hwillson self-assigned this May 26, 2020
@arminbro
Copy link

It looks like this issue/bug is introduced in version 3.0.0-beta.46

@layerssss
Copy link

layerssss commented Jun 1, 2020

@arminbro Looks like this is fixed in @apollo/client@3.0.0-beta.53

@arminbro
Copy link

arminbro commented Jun 1, 2020

@layerssss Thanks for the heads up.

fetchMore does seem to be working again in version @apollo/client@3.0.0-beta.53 but it looks like it has another issue.

The other issue:
When fetchMore is invoked, the loading prop from the useQuery or useLazyQuery doesn't seem to update with true. This is an issue because I would like to show a loading indicator to the end-user when fetching more data.

example of my useLazyQuery:

const [getQuestions, { data: questionsData, loading: loadingQuestionsData, fetchMore }] = useLazyQuery(
  GET_QUESTIONS,
  {
    fetchPolicy: 'network-only',
    notifyOnNetworkStatusChange: true,
  }
);

@benjamn, Let me know if I need to file a new bug, or if you guys can take care of this issue within this ticket.

@layerssss
Copy link

@arminbro That's true. Looks like there's one issue tracking this already #6354

@DaveClissold
Copy link

I am having this issue with rc.10 with fetch-policy set to 'no-cache' if the fetch-policy makes any difference

@benjamn
Copy link
Member

benjamn commented Jul 11, 2020

This does seem to be fixed in the latest RCs, as confirmed by @yamalight's reproduction (#6327 (comment)).

The loading status problem for fetchMore (#6354) is a known bug that we still need to fix. Feel free to leave comments over there if you have any insights/reproductions to add!

@jp928
Copy link

jp928 commented Jul 31, 2020

I have this issue on 3.1.1

@robertsmit
Copy link

robertsmit commented Aug 3, 2020

@benjamn this issue can be reopened in 3.1.1. After a fetchMore, the data is refetched.

@benjamn
Copy link
Member

benjamn commented Aug 3, 2020

@jp928 @robertsmit Are you setting notifyOnNetworkStatusChange: true?

@robertsmit
Copy link

robertsmit commented Aug 11, 2020

@benjamn
Yes, that is the case. And I use as fetchPolicy 'cache-and-network'. version 3.1.2

I currently circumvent this behaviour of the data of the original query being refetched, by using the current option values:
{
fetchPolicy: 'cache-and-network',
nextFetchPolicy: 'cache-first',
}

by using nextFetchPolicy 'cache-first' it wil not refetch the original query after calling fetchmore. But I hope that for the next releases this should not be necessary.

@benjamn
Copy link
Member

benjamn commented Aug 11, 2020

@robertsmit The nextFetchPolicy option is here to stay! We experimented with making cache-and-network automatically fall back to cache-first in #6353, but that trick caused other problems, and wasn't very predictable or intuitive.

@Alex153
Copy link

Alex153 commented Aug 24, 2020

Hi @benjamn,

I am also encountering in my app the behavior @robertsmit described. It's an 'infinite scroll' query where I would like to display a different loader when networkStatus is 3 than when it's 1.

But useQuery refetches after fetchMore being called, causing the networkStatus variable to pass from 3 to 1 and finally 7.

It seems to appear in the v3.1.0-pre.4. (Small repro here)

Just to be sure, is this behavior working as intended ?
Should we use nextFetchPolicy to counter it ? 🤔

@chrisbrooks
Copy link

Maybe a good idea to actually add nextFetchPolicy into the docs next time instead of everyone spending hours working out why the initial query just fired again...like I just did. Also how many breaking changes can you possible introduce in a new version? Migration is basically just changing the entire application zzzzzzzz.

@nadiavanleur
Copy link

I'm having this same issue with Apollo v2.6.8. Is there a fix for this issue that does not include updating to v3?

@anubhavgupta
Copy link

It seems like there is no fix, the only work around is to use nextFetchPolicy in your useQuery.

@jovenlumaas
Copy link

jovenlumaas commented May 22, 2021

After searching for hours, finally I found this temporary solution. And for the sake of other readers like me, when using nextFetchPolicy: "cache-first" doesn't trigger loading state. Just in case you need to track loading state, you have to set 'notifyOnNetworkStatusChange' to true.

@zahraazeem11
Copy link

zahraazeem11 commented Jul 6, 2021

In my case, nextFetchPolicy is not working for @apollo/client: ^3.3.19, and am still getting old data on fetchmore

    QUERY,
    {
      variables: queryVariables,
      // notifyOnNetworkStatusChange: true,
      // fetchPolicy: "network-only",
      networkStatus: networkStatusIndex,
      notifyOnNetworkStatusChange: true, 
      fetchPolicy: 'cache-and-network',
      nextFetchPolicy: 'cache-first',
    }
  );```

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests